@linxin666/dsh-pet 0.1.19 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +19 -12
  3. package/README.zh.md +19 -12
  4. package/assets/whale/pet.json +9 -0
  5. package/lib/client.js +202 -38
  6. package/lib/client.js.map +1 -1
  7. package/lib/index.js +897 -42
  8. package/lib/invariant.js +1 -1
  9. package/lib/{state-Diiq8vjk.js → state-Ch3f4luZ.js} +51 -23
  10. package/lib/types/affinity.d.ts +22 -18
  11. package/lib/types/affinity.d.ts.map +1 -1
  12. package/lib/types/affinity.js +32 -6
  13. package/lib/types/chatter.d.ts +111 -0
  14. package/lib/types/chatter.d.ts.map +1 -0
  15. package/lib/types/chatter.js +666 -0
  16. package/lib/types/client/PetDockEntry.d.ts +2 -0
  17. package/lib/types/client/PetDockEntry.d.ts.map +1 -1
  18. package/lib/types/client/PetDockEntry.js +2 -2
  19. package/lib/types/client/PetSettingsCard.d.ts.map +1 -1
  20. package/lib/types/client/PetSettingsCard.js +2 -2
  21. package/lib/types/client/PetSprite.d.ts +2 -0
  22. package/lib/types/client/PetSprite.d.ts.map +1 -1
  23. package/lib/types/client/PetSprite.js +100 -25
  24. package/lib/types/client/PluginSettingsCard.d.ts +7 -1
  25. package/lib/types/client/PluginSettingsCard.d.ts.map +1 -1
  26. package/lib/types/client/PluginSettingsCard.js +5 -3
  27. package/lib/types/client/index.d.ts +1 -1
  28. package/lib/types/client/index.d.ts.map +1 -1
  29. package/lib/types/client/index.js +31 -6
  30. package/lib/types/client/locales.d.ts +82 -80
  31. package/lib/types/client/locales.d.ts.map +1 -1
  32. package/lib/types/client/locales.js +2 -0
  33. package/lib/types/client/sequences.d.ts +10 -0
  34. package/lib/types/client/sequences.d.ts.map +1 -0
  35. package/lib/types/client/sequences.js +20 -0
  36. package/lib/types/client/settings-form.d.ts.map +1 -1
  37. package/lib/types/client/spritesheet.d.ts +1 -1
  38. package/lib/types/client/spritesheet.d.ts.map +1 -1
  39. package/lib/types/client/spritesheet.js +3 -12
  40. package/lib/types/event-projection.d.ts +14 -1
  41. package/lib/types/event-projection.d.ts.map +1 -1
  42. package/lib/types/event-projection.js +38 -17
  43. package/lib/types/index.d.ts +2 -1
  44. package/lib/types/index.d.ts.map +1 -1
  45. package/lib/types/index.js +15 -13
  46. package/lib/types/invariant.js +2 -2
  47. package/lib/types/ledger.d.ts.map +1 -1
  48. package/lib/types/ledger.js +18 -10
  49. package/lib/types/mount-once.d.ts +25 -0
  50. package/lib/types/mount-once.d.ts.map +1 -0
  51. package/lib/types/mount-once.js +42 -0
  52. package/lib/types/persist.d.ts.map +1 -1
  53. package/lib/types/persist.js +6 -4
  54. package/lib/types/registry.d.ts +7 -1
  55. package/lib/types/registry.d.ts.map +1 -1
  56. package/lib/types/registry.js +37 -1
  57. package/lib/types/remarks.d.ts +2 -0
  58. package/lib/types/remarks.d.ts.map +1 -1
  59. package/lib/types/remarks.js +5 -0
  60. package/lib/types/routes.js +2 -2
  61. package/lib/types/service.d.ts +16 -6
  62. package/lib/types/service.d.ts.map +1 -1
  63. package/lib/types/service.js +26 -8
  64. package/lib/types/state.d.ts +7 -4
  65. package/lib/types/state.d.ts.map +1 -1
  66. package/lib/types/state.js +20 -20
  67. package/package.json +9 -9
  68. package/src/affinity.test.ts +4 -2
  69. package/src/affinity.ts +37 -6
  70. package/src/chatter.test.ts +154 -0
  71. package/src/chatter.ts +697 -0
  72. package/src/client/PetDockEntry.tsx +3 -0
  73. package/src/client/PetSprite.test.tsx +246 -4
  74. package/src/client/PetSprite.tsx +132 -36
  75. package/src/client/PluginSettingsCard.tsx +10 -0
  76. package/src/client/index.ts +26 -3
  77. package/src/client/locales.ts +2 -0
  78. package/src/client/pet-css.test.ts +42 -0
  79. package/src/client/pet.module.css +173 -22
  80. package/src/client/sequences.test.ts +33 -0
  81. package/src/client/sequences.ts +33 -0
  82. package/src/client/spritesheet.ts +3 -13
  83. package/src/event-projection.ts +49 -16
  84. package/src/index.ts +4 -1
  85. package/src/ledger.test.ts +42 -1
  86. package/src/ledger.ts +15 -7
  87. package/src/mount-once.ts +48 -0
  88. package/src/persist.test.ts +18 -1
  89. package/src/persist.ts +3 -1
  90. package/src/registry.test.ts +50 -3
  91. package/src/registry.ts +47 -3
  92. package/src/remarks.ts +6 -0
  93. package/src/service.ts +42 -9
  94. package/src/state.test.ts +13 -0
  95. package/src/state.ts +24 -18
@@ -6,9 +6,9 @@
6
6
  * economic events: completed-turn rewards (official or legacy) and feeds.
7
7
  * @module @linxin666/dsh-pet/ledger
8
8
  */
9
- import { applyInteraction, affinityViewOf, applyTurnReward, defaultAffinityConfig, } from "./affinity.js";
10
- import { consumeTreat, defaultTreatConfig, settleTreatGrants, } from "./treats.js";
11
- import { RemarkPicker } from "./remarks.js";
9
+ import { applyInteraction, affinityViewOf, applyTurnReward, defaultAffinityConfig, } from './affinity.js';
10
+ import { consumeTreat, defaultTreatConfig, settleTreatGrants, } from './treats.js';
11
+ import { RemarkPicker } from './remarks.js';
12
12
  /**
13
13
  * Holds the current persistence snapshot and all economy bookkeeping. Every
14
14
  * mutating call flags takeDirty so the facade persists exactly once per
@@ -135,12 +135,19 @@ export class PetLedger {
135
135
  interact(kind, nowMs) {
136
136
  if (kind === 'feed')
137
137
  this.settleTreats(nowMs);
138
- const outcome = applyInteraction(this.current.affinity, kind, nowMs, this.affinityConfig);
138
+ const before = this.current.affinity;
139
+ const outcome = applyInteraction(before, kind, nowMs, this.affinityConfig);
139
140
  // Reactions come from the picker pools (custom per-pet slots override
140
141
  // the built-in library per slot); outcome.reaction stays the fallback
141
142
  // copy for direct applyInteraction callers.
142
143
  if (kind === 'feed' && !outcome.accepted) {
143
- return { reaction: this.picker.pick('feedCooldown'), delta: 0, affinity: this.affinityView(nowMs) };
144
+ this.current = { ...this.current, affinity: outcome.affinity };
145
+ this.dirty = true;
146
+ return {
147
+ reaction: this.picker.pickAt('feedCooldown', before.feedRejects),
148
+ delta: 0,
149
+ affinity: this.affinityView(nowMs),
150
+ };
144
151
  }
145
152
  if (kind === 'feed') {
146
153
  const consume = consumeTreat(this.current.treats);
@@ -154,12 +161,13 @@ export class PetLedger {
154
161
  this.current = { ...this.current, treats: consume.ledger };
155
162
  this.dirty = true;
156
163
  }
157
- if (outcome.accepted) {
158
- this.current = { ...this.current, affinity: outcome.affinity };
159
- this.dirty = true;
160
- }
164
+ this.current = { ...this.current, affinity: outcome.affinity };
165
+ this.dirty = true;
166
+ const count = kind === 'pet'
167
+ ? outcome.accepted ? before.pets : before.petRejects
168
+ : before.feeds;
161
169
  return {
162
- reaction: this.picker.pick(outcome.accepted ? kind : 'petCooldown'),
170
+ reaction: this.picker.pickAt(outcome.accepted ? kind : 'petCooldown', count),
163
171
  delta: outcome.delta,
164
172
  affinity: this.affinityView(nowMs),
165
173
  };
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Host single-instance guard shared by the plugin family. The family bundle
3
+ * (dsh-web-ui-all / dsh-skins) namespaces every child row id (web-ui-*), so
4
+ * the loader accepts a standalone install of the same package side by side;
5
+ * without this guard the second instance would still re-register the same
6
+ * webserver routes, tools, settings namespaces, and system-prompt sections
7
+ * and fail the boot. mountOnce makes the second host apply a no-op for the
8
+ * lifetime of the first instance (the browser half is already deduped by
9
+ * package name in the client module host).
10
+ *
11
+ * The registry rides a global symbol so two module instances of the same
12
+ * package (npm copy vs repository link) still share one verdict. cordis
13
+ * `ctx.effect` runs its callback immediately and treats the callback's
14
+ * return value as the fiber disposer, so the unmarker is returned, not run.
15
+ */
16
+ /**
17
+ * Wrap a cordis plugin apply so the package runs at most once per process.
18
+ * The first mount registers normally and unmarks when its fiber disposes;
19
+ * any later mount of the same package name is a no-op.
20
+ * @param packageName - npm package identity shared by every install source.
21
+ * @param fn - the original plugin apply.
22
+ * @returns an apply of the same shape.
23
+ */
24
+ export declare function mountOnce<T extends (...args: any[]) => unknown>(packageName: string, fn: T): T;
25
+ //# sourceMappingURL=mount-once.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mount-once.d.ts","sourceRoot":"","sources":["../../src/mount-once.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;GAcG;AAaH;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,CAW9F"}
@@ -0,0 +1,42 @@
1
+ // Generated by scripts/sync-shared.mjs from shared/host/mount-once.ts. Do not edit this copy; edit the shared source and run "node scripts/sync-shared.mjs".
2
+ /**
3
+ * Host single-instance guard shared by the plugin family. The family bundle
4
+ * (dsh-web-ui-all / dsh-skins) namespaces every child row id (web-ui-*), so
5
+ * the loader accepts a standalone install of the same package side by side;
6
+ * without this guard the second instance would still re-register the same
7
+ * webserver routes, tools, settings namespaces, and system-prompt sections
8
+ * and fail the boot. mountOnce makes the second host apply a no-op for the
9
+ * lifetime of the first instance (the browser half is already deduped by
10
+ * package name in the client module host).
11
+ *
12
+ * The registry rides a global symbol so two module instances of the same
13
+ * package (npm copy vs repository link) still share one verdict. cordis
14
+ * `ctx.effect` runs its callback immediately and treats the callback's
15
+ * return value as the fiber disposer, so the unmarker is returned, not run.
16
+ */
17
+ const MOUNTED = Symbol.for('dsh-web-ui.mounted-plugins');
18
+ function mountedSet() {
19
+ const registry = globalThis;
20
+ return (registry[MOUNTED] ??= new Set());
21
+ }
22
+ /**
23
+ * Wrap a cordis plugin apply so the package runs at most once per process.
24
+ * The first mount registers normally and unmarks when its fiber disposes;
25
+ * any later mount of the same package name is a no-op.
26
+ * @param packageName - npm package identity shared by every install source.
27
+ * @param fn - the original plugin apply.
28
+ * @returns an apply of the same shape.
29
+ */
30
+ export function mountOnce(packageName, fn) {
31
+ return ((...args) => {
32
+ const mounted = mountedSet();
33
+ if (mounted.has(packageName))
34
+ return;
35
+ mounted.add(packageName);
36
+ const ctx = args[0];
37
+ ctx?.effect?.(() => () => {
38
+ mounted.delete(packageName);
39
+ });
40
+ return fn(...args);
41
+ });
42
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"persist.d.ts","sourceRoot":"","sources":["../../src/persist.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAA+B,KAAK,aAAa,EAAE,MAAM,eAAe,CAAA;AAC/E,OAAO,EAAwC,KAAK,WAAW,EAAE,MAAM,aAAa,CAAA;AAEpF,gDAAgD;AAChD,MAAM,WAAW,gBAAgB;IAC/B,qBAAqB;IACrB,OAAO,EAAE,OAAO,CAAA;IAChB,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAA;IACZ,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAA;IACb,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAA;CACf;AAED,eAAO,MAAM,oBAAoB,EAAE,gBAKlC,CAAA;AAED,2EAA2E;AAC3E,eAAO,MAAM,gBAAgB,KAAK,CAAA;AAClC,eAAO,MAAM,gBAAgB,MAAM,CAAA;AACnC,eAAO,MAAM,iBAAiB,QAAS,CAAA;AAEvC,wCAAwC;AACxC,MAAM,WAAW,UAAU;IACzB,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,QAAQ,EAAE,aAAa,CAAA;IACvB,gCAAgC;IAChC,MAAM,EAAE,WAAW,CAAA;IACnB,OAAO,EAAE,gBAAgB,CAAA;CAC1B;AAED,qEAAqE;AACrE,eAAO,MAAM,cAAc,eAAe,CAAA;AAE1C,2EAA2E;AAC3E,eAAO,MAAM,gBAAgB,uBAAQ,CAAA;AAErC,wBAAwB;AACxB,eAAO,MAAM,mBAAmB,KAAK,CAAA;AAErC,wBAAgB,YAAY,IAAI,UAAU,CAQzC;AAED;;;;GAIG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AA4BD,4EAA4E;AAC5E,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAqB,GAAG,UAAU,CAiDrE;AAED,sDAAsD;AACtD,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,GAAE,MAAqB,GAAG,IAAI,CAMjF"}
1
+ {"version":3,"file":"persist.d.ts","sourceRoot":"","sources":["../../src/persist.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAA+B,KAAK,aAAa,EAAE,MAAM,eAAe,CAAA;AAC/E,OAAO,EAAwC,KAAK,WAAW,EAAE,MAAM,aAAa,CAAA;AAEpF,gDAAgD;AAChD,MAAM,WAAW,gBAAgB;IAC/B,qBAAqB;IACrB,OAAO,EAAE,OAAO,CAAA;IAChB,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAA;IACZ,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAA;IACb,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAA;CACf;AAED,eAAO,MAAM,oBAAoB,EAAE,gBAKlC,CAAA;AAED,2EAA2E;AAC3E,eAAO,MAAM,gBAAgB,KAAK,CAAA;AAClC,eAAO,MAAM,gBAAgB,MAAM,CAAA;AACnC,eAAO,MAAM,iBAAiB,QAAS,CAAA;AAEvC,wCAAwC;AACxC,MAAM,WAAW,UAAU;IACzB,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,QAAQ,EAAE,aAAa,CAAA;IACvB,gCAAgC;IAChC,MAAM,EAAE,WAAW,CAAA;IACnB,OAAO,EAAE,gBAAgB,CAAA;CAC1B;AAED,qEAAqE;AACrE,eAAO,MAAM,cAAc,eAAe,CAAA;AAE1C,2EAA2E;AAC3E,eAAO,MAAM,gBAAgB,uBAAQ,CAAA;AAErC,wBAAwB;AACxB,eAAO,MAAM,mBAAmB,KAAK,CAAA;AAErC,wBAAgB,YAAY,IAAI,UAAU,CAQzC;AAED;;;;GAIG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AA4BD,4EAA4E;AAC5E,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAqB,GAAG,UAAU,CAmDrE;AAED,sDAAsD;AACtD,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,GAAE,MAAqB,GAAG,IAAI,CAMjF"}
@@ -6,14 +6,14 @@
6
6
  */
7
7
  import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
8
8
  import { join } from 'node:path';
9
- import { dshHome } from "./dsh-home.js";
10
- import { AFFINITY_MAX, emptyAffinity } from "./affinity.js";
11
- import { defaultTreatConfig, emptyTreatLedger } from "./treats.js";
9
+ import { dshHome } from './dsh-home.js';
10
+ import { AFFINITY_MAX, emptyAffinity } from './affinity.js';
11
+ import { defaultTreatConfig, emptyTreatLedger } from './treats.js';
12
12
  export const defaultDisplayConfig = {
13
13
  visible: true,
14
14
  size: 160,
15
15
  right: 24,
16
- bottom: 20,
16
+ bottom: 120,
17
17
  };
18
18
  /** Display value bounds (shared by load-time validation and setConfig). */
19
19
  export const DISPLAY_SIZE_MIN = 32;
@@ -78,6 +78,8 @@ export function loadPetPersist(dir = petHomeDir()) {
78
78
  lastFeedAt: clamp(finiteNum(rawAffinity.lastFeedAt, 0), Number.MAX_SAFE_INTEGER),
79
79
  pets: clamp(finiteNum(rawAffinity.pets, 0), Number.MAX_SAFE_INTEGER),
80
80
  feeds: clamp(finiteNum(rawAffinity.feeds, 0), Number.MAX_SAFE_INTEGER),
81
+ petRejects: clamp(finiteNum(rawAffinity.petRejects, 0), Number.MAX_SAFE_INTEGER),
82
+ feedRejects: clamp(finiteNum(rawAffinity.feedRejects, 0), Number.MAX_SAFE_INTEGER),
81
83
  turns: clamp(finiteNum(rawAffinity.turns, 0), Number.MAX_SAFE_INTEGER),
82
84
  };
83
85
  const rawTreats = (parsed.treats ?? {});
@@ -16,7 +16,7 @@
16
16
  * whale-girl manifest overrides its own durations.
17
17
  * @module @linxin666/dsh-pet/registry
18
18
  */
19
- import type { PetAnimation } from './state.ts';
19
+ import type { ActivityPhase, PetAnimation } from './state.ts';
20
20
  import { type PetRemarks, type PetRemarksManifest } from './remarks.ts';
21
21
  /** Fixed row order of the 9-state animation contract. */
22
22
  export declare const PET_ROW_ORDER: readonly PetAnimation[];
@@ -81,6 +81,8 @@ export interface PetManifest {
81
81
  frames?: number[];
82
82
  /** Optional per-track rhythm overrides; omitted tracks use the defaults. */
83
83
  tracks?: Partial<Record<PetAnimation, PetTrackOverride>>;
84
+ /** Optional per-scene track sequences; every declared sequence has at least 5 items. */
85
+ sequences?: Partial<Record<ActivityPhase, PetAnimation[]>>;
84
86
  /**
85
87
  * Optional witty-remark overrides the pet speaks on interactions
86
88
  * (community contributions use this to give their pet its own voice).
@@ -109,8 +111,12 @@ export interface PetDefinition {
109
111
  columns: number;
110
112
  /** Per-row frame counts (length 9, row order above). */
111
113
  rows: number[];
114
+ /** Total atlas rows (9 for v1, 11 for v2 look-row atlases). */
115
+ atlasRows: number;
112
116
  /** Fully resolved animation tracks (frames + durations + loop/fallback). */
113
117
  tracks: Record<PetAnimation, PetTrackDef>;
118
+ /** Validated per-scene track sequences; omitted scenes keep single-track playback. */
119
+ sequences?: Partial<Record<ActivityPhase, PetAnimation[]>>;
114
120
  /** Browser URL of the atlas (served by the host asset route). */
115
121
  atlasUrl: string;
116
122
  /** Browser URL of the manifest (served by the host asset route). */
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/registry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/registry.ts"],"names":[],"mappings":"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"}
@@ -20,7 +20,7 @@ import { existsSync, readdirSync, readFileSync } from 'node:fs';
20
20
  import { homedir } from 'node:os';
21
21
  import { basename, dirname, isAbsolute, join, resolve } from 'node:path';
22
22
  import { fileURLToPath } from 'node:url';
23
- import { normalizePetRemarks } from "./remarks.js";
23
+ import { normalizePetRemarks } from './remarks.js';
24
24
  /** Fixed row order of the 9-state animation contract. */
25
25
  export const PET_ROW_ORDER = [
26
26
  'idle',
@@ -86,6 +86,35 @@ const PET_ID_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
86
86
  /** Safe path-segment charset for atlas files. */
87
87
  const PATH_SEGMENT_PATTERN = /^[A-Za-z0-9._-]+$/;
88
88
  const PET_NAME_MAX_LENGTH = 80;
89
+ const PET_PHASES = ['idle', 'waiting', 'thinking', 'tool', 'review', 'done', 'failed'];
90
+ /** Validate optional scene sequences without rejecting an otherwise usable pet. */
91
+ function normalizeSequences(raw, id, warn) {
92
+ if (raw === undefined)
93
+ return undefined;
94
+ if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
95
+ warn('manifest ' + id + ': sequences must be an object keyed by activity phase');
96
+ return undefined;
97
+ }
98
+ const sequences = {};
99
+ for (const [phase, value] of Object.entries(raw)) {
100
+ if (!PET_PHASES.includes(phase)) {
101
+ warn('manifest ' + id + ': unknown sequence phase ' + JSON.stringify(phase));
102
+ continue;
103
+ }
104
+ if (!Array.isArray(value) || value.length < 5) {
105
+ warn('manifest ' + id + ': sequence ' + phase + ' must contain at least 5 animations');
106
+ continue;
107
+ }
108
+ const unknownIndex = value.findIndex(animation => typeof animation !== 'string' || !PET_ROW_ORDER.includes(animation));
109
+ if (unknownIndex !== -1) {
110
+ const unknown = value[unknownIndex];
111
+ warn('manifest ' + id + ': sequence ' + phase + ' contains unknown animation ' + JSON.stringify(unknown));
112
+ continue;
113
+ }
114
+ sequences[phase] = value;
115
+ }
116
+ return Object.keys(sequences).length === 0 ? undefined : sequences;
117
+ }
89
118
  /**
90
119
  * Normalize one parsed manifest into a renderable pet entry, or undefined
91
120
  * (with a warning recorded) when the manifest violates the contract.
@@ -126,11 +155,14 @@ export function resolvePetManifest(raw, dir, options = {}) {
126
155
  height: finiteInt(rawCell.height, DEFAULT_PET_CELL.height, 2048),
127
156
  };
128
157
  const columns = finiteInt(source.columns, DEFAULT_PET_COLUMNS, 32);
158
+ // v2 atlases (spriteVersionNumber 2) hold 11 rows: 9 animation rows + 2 look rows.
159
+ const atlasRowCount = source.spriteVersionNumber === 2 ? 11 : DEFAULT_PET_ROW_COUNT;
129
160
  const rows = DEFAULT_FRAME_COUNTS.map((fallback, index) => {
130
161
  const value = Array.isArray(source.frames) ? source.frames[index] : undefined;
131
162
  return finiteInt(value, fallback, columns);
132
163
  });
133
164
  const remarks = normalizePetRemarks(source.remarks, message => warn('manifest ' + id + ': ' + message));
165
+ const sequences = normalizeSequences(source.sequences, id, warn);
134
166
  const trackOverrides = (typeof source.tracks === 'object' && source.tracks !== null ? source.tracks : {});
135
167
  const tracks = {};
136
168
  for (const [row, animation] of PET_ROW_ORDER.entries()) {
@@ -165,7 +197,9 @@ export function resolvePetManifest(raw, dir, options = {}) {
165
197
  cell,
166
198
  columns,
167
199
  rows,
200
+ atlasRows: atlasRowCount,
168
201
  tracks,
202
+ ...(sequences === undefined ? {} : { sequences }),
169
203
  atlasUrl: assetUrl(assetPrefix, id, spritesheet),
170
204
  manifestUrl: assetUrl(assetPrefix, id, 'pet.json'),
171
205
  dir,
@@ -264,7 +298,9 @@ export function petEntryView(entry) {
264
298
  cell: entry.cell,
265
299
  columns: entry.columns,
266
300
  rows: entry.rows,
301
+ atlasRows: entry.atlasRows,
267
302
  tracks: entry.tracks,
303
+ ...(entry.sequences === undefined ? {} : { sequences: entry.sequences }),
268
304
  atlasUrl: entry.atlasUrl,
269
305
  manifestUrl: entry.manifestUrl,
270
306
  };
@@ -52,5 +52,7 @@ export declare class RemarkPicker {
52
52
  pool(kind: RemarkKind): readonly string[];
53
53
  /** The next line for one slot (round-robin within its pool). */
54
54
  pick(kind: RemarkKind): string;
55
+ /** Select a line from a stable external counter without changing local picker state. */
56
+ pickAt(kind: RemarkKind, count: number): string;
55
57
  }
56
58
  //# sourceMappingURL=remarks.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"remarks.d.ts","sourceRoot":"","sources":["../../src/remarks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,wDAAwD;AACxD,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,aAAa,GAAG,MAAM,GAAG,cAAc,GAAG,UAAU,CAAA;AAErF,4CAA4C;AAC5C,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAM7C,CAAA;AAED,6EAA6E;AAC7E,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC,CAAA;AAE9D,yEAAyE;AACzE,MAAM,MAAM,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC,CAAA;AAE/E,2EAA2E;AAC3E,eAAO,MAAM,eAAe,MAAM,CAAA;AAClC,sCAAsC;AACtC,eAAO,MAAM,gBAAgB,KAAK,CAAA;AAElC;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC,CA6D3E,CAAA;AAED,yEAAyE;AACzE,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAEtD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,OAAO,EACZ,SAAS,GAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAe,GAC9C,UAAU,GAAG,SAAS,CAyBxB;AAED;;;;GAIG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgC;IACzD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuC;gBAEjD,SAAS,CAAC,EAAE,UAAU;IAQlC,qEAAqE;IACrE,IAAI,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS,MAAM,EAAE;IAIzC,gEAAgE;IAChE,IAAI,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM;CAM/B"}
1
+ {"version":3,"file":"remarks.d.ts","sourceRoot":"","sources":["../../src/remarks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,wDAAwD;AACxD,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,aAAa,GAAG,MAAM,GAAG,cAAc,GAAG,UAAU,CAAA;AAErF,4CAA4C;AAC5C,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAM7C,CAAA;AAED,6EAA6E;AAC7E,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC,CAAA;AAE9D,yEAAyE;AACzE,MAAM,MAAM,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC,CAAA;AAE/E,2EAA2E;AAC3E,eAAO,MAAM,eAAe,MAAM,CAAA;AAClC,sCAAsC;AACtC,eAAO,MAAM,gBAAgB,KAAK,CAAA;AAElC;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC,CA6D3E,CAAA;AAED,yEAAyE;AACzE,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAEtD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,OAAO,EACZ,SAAS,GAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAe,GAC9C,UAAU,GAAG,SAAS,CAyBxB;AAED;;;;GAIG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgC;IACzD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuC;IAE7D,YAAY,SAAS,CAAC,EAAE,UAAU,EAMjC;IAED,qEAAqE;IACrE,IAAI,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS,MAAM,EAAE,CAExC;IAED,gEAAgE;IAChE,IAAI,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAK7B;IAED,wFAAwF;IACxF,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAG9C;CACF"}
@@ -154,4 +154,9 @@ export class RemarkPicker {
154
154
  this.counters.set(kind, index + 1);
155
155
  return pool[index];
156
156
  }
157
+ /** Select a line from a stable external counter without changing local picker state. */
158
+ pickAt(kind, count) {
159
+ const pool = this.pools[kind];
160
+ return pool[Math.max(0, Math.floor(count)) % pool.length];
161
+ }
157
162
  }
@@ -11,7 +11,7 @@
11
11
  import { existsSync } from 'node:fs';
12
12
  import { readFile } from 'node:fs/promises';
13
13
  import { join } from 'node:path';
14
- import { petEntryView } from "./registry.js";
14
+ import { petEntryView } from './registry.js';
15
15
  /** Browser-facing base path of the pet API. */
16
16
  export const PET_API_PREFIX = '/api/pet';
17
17
  /** Browser-facing base path of the pet asset routes ('/pet/<id>/...'). */
@@ -273,4 +273,4 @@ export function makePetRoutes(deps) {
273
273
  return [...apiRoutes, assetRoute];
274
274
  }
275
275
  // Re-exported for the package surface (the registry owns the definition now).
276
- export { petPackageRoot } from "./registry.js";
276
+ export { petPackageRoot } from './registry.js';
@@ -59,9 +59,12 @@ export interface PetSettingsSection {
59
59
  /** Settings namespace of the pet capability. Spelled here rather than imported: the browser half spells the same value. */
60
60
  export declare const PET_SETTINGS_NAMESPACE = "pet";
61
61
  /**
62
- * One active session as the pet displays it. Sessions run in parallel, so
63
- * each gets its own bubble while the sprite itself follows the most recent
64
- * meaningful event (the display session).
62
+ * One active TOP-LEVEL session as the pet displays it. Sessions run in
63
+ * parallel, so each gets its own bubble while the sprite itself follows the
64
+ * most recent meaningful event (the display session). Subagent children
65
+ * report no bubble of their own: their work is already reflected by the
66
+ * bubble of the conversation that spawned them, and the bubble buttons
67
+ * navigate to GUI sessions, which subagents are not.
65
68
  */
66
69
  export interface PetSessionView {
67
70
  /** Session identity (stringified for the wire; never exposed as a key). */
@@ -82,9 +85,10 @@ export interface PetStateView {
82
85
  phase: PetStateSnapshot['phase'];
83
86
  sessionActive: boolean;
84
87
  /**
85
- * Per-session bubbles for every concurrently active session, most recent
86
- * first; optional so older hosts without the multi-session view stay
87
- * consumable. The single 'bubble' above mirrors the display session.
88
+ * Per-session bubbles for every concurrently active TOP-LEVEL session,
89
+ * most recent first; optional so older hosts without the multi-session
90
+ * view stay consumable. The single 'bubble' above mirrors the display
91
+ * session.
88
92
  */
89
93
  sessions?: PetSessionView[];
90
94
  /** Affinity ledger snapshot. */
@@ -109,6 +113,12 @@ export interface PetStateView {
109
113
  /** Stock cap. */
110
114
  max: number;
111
115
  };
116
+ /**
117
+ * The display session's fresh inner whisper (碎碎念), when one is within
118
+ * its TTL — short inner-voice copy woken by the model's own output,
119
+ * rendered by the client as a distinct whisper bubble.
120
+ */
121
+ whisper?: string;
112
122
  }
113
123
  /** Result of `pet.interact`. */
114
124
  export type PetInteractResult = LedgerInteractionResult;
@@ -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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"}
@@ -12,11 +12,12 @@
12
12
  * @module @linxin666/dsh-pet/service
13
13
  */
14
14
  import { Service } from '@deepseek-ai/cordis';
15
- import { emptyProjectionRuntime, isActivityPhase, projectOfficialEvent, } from "./event-projection.js";
16
- import { PetLedger } from "./ledger.js";
17
- import { DEFAULT_PET_NAME, DISPLAY_INSET_MAX, DISPLAY_SIZE_MAX, DISPLAY_SIZE_MIN, PET_NAME_MAX_LENGTH, loadPetPersist, petHomeDir, savePetPersist, } from "./persist.js";
18
- import { loadPetRegistry, petEntryView, petPackageRoot, } from "./registry.js";
19
- import { defaultPetStateConfig, PetStateMachine, } from "./state.js";
15
+ import { emptyProjectionRuntime, isActivityPhase, projectOfficialEvent, } from './event-projection.js';
16
+ import { PetLedger } from './ledger.js';
17
+ import { DEFAULT_PET_NAME, DISPLAY_INSET_MAX, DISPLAY_SIZE_MAX, DISPLAY_SIZE_MIN, PET_NAME_MAX_LENGTH, loadPetPersist, petHomeDir, savePetPersist, } from './persist.js';
18
+ import { loadPetRegistry, petEntryView, petPackageRoot, } from './registry.js';
19
+ import { WHISPER_TTL_MS } from './chatter.js';
20
+ import { defaultPetStateConfig, PetStateMachine, } from './state.js';
20
21
  /** Settings namespace of the pet capability. Spelled here rather than imported: the browser half spells the same value. */
21
22
  export const PET_SETTINGS_NAMESPACE = 'pet';
22
23
  /** Hard cap on simultaneously displayed session bubbles (most recent first). */
@@ -159,7 +160,7 @@ export class PetService extends Service {
159
160
  if (transition === undefined)
160
161
  return;
161
162
  runtime.officialEventsSeen = true;
162
- this.applyActivity(session, transition.input);
163
+ this.applyActivity(session, transition.input, transition.whisper);
163
164
  if (transition.completedTurn !== undefined) {
164
165
  this.rewardTurn(String(session.id), transition.completedTurn);
165
166
  }
@@ -206,9 +207,11 @@ export class PetService extends Service {
206
207
  * the session becomes the host-global display session (most recent
207
208
  * meaningful event wins the sprite animation).
208
209
  */
209
- applyActivity(session, input) {
210
+ applyActivity(session, input, whisper) {
210
211
  const activity = this.activityOf(session);
211
212
  activity.lastInput = input;
213
+ if (whisper !== undefined)
214
+ activity.whisper = { text: whisper, at: Date.now() };
212
215
  activity.machine.onActivityStatus(input);
213
216
  activity.machine.onSessionActive();
214
217
  // Move to the tail so map order reads most-recent-last, then trim the
@@ -317,13 +320,18 @@ export class PetService extends Service {
317
320
  view() {
318
321
  const snapshot = this.machine.render();
319
322
  const entry = this.activeEntry();
320
- // One bubble per concurrently active session, most recent first.
323
+ // One bubble per concurrently active TOP-LEVEL session, most recent
324
+ // first. Subagent children render no bubble of their own (their activity
325
+ // already shows through the spawning conversation's bubble/display, and
326
+ // the bubble buttons navigate to GUI sessions, which subagents are not).
321
327
  // Sessions whose own machine has settled (no bubble copy) drop out, so a
322
328
  // finished turn does not leave a stale bubble behind.
323
329
  const sessions = [];
324
330
  for (const [session, activity] of [...this.sessionActivity.entries()].reverse()) {
325
331
  if (sessions.length >= MAX_SESSION_BUBBLES)
326
332
  break;
333
+ if (session.header?.origin === 'subagent')
334
+ continue;
327
335
  const perSession = activity.machine.render();
328
336
  if (perSession.bubble === undefined)
329
337
  continue;
@@ -334,6 +342,15 @@ export class PetService extends Service {
334
342
  phase: perSession.phase,
335
343
  });
336
344
  }
345
+ // The display session's inner whisper rides the global view while fresh;
346
+ // an expired whisper simply stops appearing (the client's 2s poll drops it).
347
+ const displayActivity = this.displaySession === undefined
348
+ ? undefined
349
+ : this.sessionActivity.get(this.displaySession);
350
+ const whisper = displayActivity?.whisper;
351
+ const freshWhisper = whisper !== undefined && Date.now() - whisper.at < WHISPER_TTL_MS
352
+ ? whisper.text
353
+ : undefined;
337
354
  // Read-only: the ledger settles on economic events only, never on a read,
338
355
  // so polling the state cannot trigger pet.json writes.
339
356
  return {
@@ -342,6 +359,7 @@ export class PetService extends Service {
342
359
  phase: snapshot.phase,
343
360
  sessionActive: snapshot.sessionActive,
344
361
  sessions,
362
+ ...(freshWhisper === undefined ? {} : { whisper: freshWhisper }),
345
363
  affinity: this.ledger.affinityView(Date.now()),
346
364
  display: { ...this.ledger.snapshot.display },
347
365
  pet: {
@@ -39,6 +39,8 @@ export interface PetStateSnapshot {
39
39
  export interface PetStateConfig {
40
40
  /** Celebration window after `done` before settling to idle, ms (default 2400). */
41
41
  celebrateMs: number;
42
+ /** Failure display window before settling to idle, ms (default 2400). */
43
+ failureMs: number;
42
44
  }
43
45
  export declare const defaultPetStateConfig: PetStateConfig;
44
46
  /**
@@ -47,7 +49,7 @@ export declare const defaultPetStateConfig: PetStateConfig;
47
49
  * - review → `review` while answer text is streaming.
48
50
  * - waiting → `waiting` (expectant pose, needs user input).
49
51
  * - done → `jumping` (celebration), then back to `idle` after the window.
50
- * - failed → `failed` until another activity event arrives.
52
+ * - failed → `failed` briefly, then back to `idle`.
51
53
  * - idle → `idle` (calm breathing loop).
52
54
  */
53
55
  export declare function animationForPhase(phase: ActivityPhase): PetAnimation;
@@ -55,17 +57,18 @@ export declare function animationForPhase(phase: ActivityPhase): PetAnimation;
55
57
  export declare function rowOf(animation: PetAnimation): number;
56
58
  /**
57
59
  * PetStateMachine — one instance per host process. Holds only the latest
58
- * input snapshot and the celebration timing; no storage, no side effects.
60
+ * input snapshot and terminal-state timing; no storage, no side effects.
59
61
  */
60
62
  export declare class PetStateMachine {
61
- private readonly config;
62
63
  private readonly now;
63
64
  private phase;
64
65
  private line;
65
66
  private phrase;
66
67
  private sessionActive;
67
68
  private doneAt;
68
- constructor(config?: PetStateConfig, now?: () => number);
69
+ private failedAt;
70
+ private readonly config;
71
+ constructor(config?: Partial<PetStateConfig>, now?: () => number);
69
72
  /** Consume one projected activity update. */
70
73
  onActivityStatus(input: PetStateInput): void;
71
74
  /** A session became the active one (or a fresh session started). */
@@ -1 +1 @@
1
- {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,kDAAkD;AAClD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAA;AAEnG,0EAA0E;AAC1E,MAAM,MAAM,YAAY,GACpB,MAAM,GACN,eAAe,GACf,cAAc,GACd,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,SAAS,GACT,SAAS,GACT,QAAQ,CAAA;AAEZ,kDAAkD;AAClD,MAAM,WAAW,aAAa;IAC5B,oDAAoD;IACpD,KAAK,EAAE,aAAa,CAAA;IACpB,+CAA+C;IAC/C,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,4DAA4D;AAC5D,MAAM,WAAW,gBAAgB;IAC/B,qCAAqC;IACrC,SAAS,EAAE,YAAY,CAAA;IACvB,wEAAwE;IACxE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,oEAAoE;IACpE,kBAAkB,EAAE,MAAM,CAAA;IAC1B,oEAAoE;IACpE,KAAK,EAAE,aAAa,CAAA;IACpB,0DAA0D;IAC1D,aAAa,EAAE,OAAO,CAAA;CACvB;AAED,6BAA6B;AAC7B,MAAM,WAAW,cAAc;IAC7B,kFAAkF;IAClF,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,eAAO,MAAM,qBAAqB,EAAE,cAAsC,CAAA;AAE1E;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,GAAG,YAAY,CAUpE;AAED,yDAAyD;AACzD,wBAAgB,KAAK,CAAC,SAAS,EAAE,YAAY,GAAG,MAAM,CAarD;AAED;;;GAGG;AACH,qBAAa,eAAe;IAQxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG;IARtB,OAAO,CAAC,KAAK,CAAwB;IACrC,OAAO,CAAC,IAAI,CAAoB;IAChC,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,MAAM,CAAoB;gBAGf,MAAM,GAAE,cAAsC,EAC9C,GAAG,GAAE,MAAM,MAAiB;IAG/C,6CAA6C;IAC7C,gBAAgB,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAO5C,oEAAoE;IACpE,eAAe,IAAI,IAAI;IAIvB,sDAAsD;IACtD,iBAAiB,IAAI,IAAI;IAQzB,6CAA6C;IAC7C,MAAM,IAAI,gBAAgB;CAwB3B"}
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,kDAAkD;AAClD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAA;AAEnG,0EAA0E;AAC1E,MAAM,MAAM,YAAY,GACpB,MAAM,GACN,eAAe,GACf,cAAc,GACd,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,SAAS,GACT,SAAS,GACT,QAAQ,CAAA;AAEZ,kDAAkD;AAClD,MAAM,WAAW,aAAa;IAC5B,oDAAoD;IACpD,KAAK,EAAE,aAAa,CAAA;IACpB,+CAA+C;IAC/C,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,4DAA4D;AAC5D,MAAM,WAAW,gBAAgB;IAC/B,qCAAqC;IACrC,SAAS,EAAE,YAAY,CAAA;IACvB,wEAAwE;IACxE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,oEAAoE;IACpE,kBAAkB,EAAE,MAAM,CAAA;IAC1B,oEAAoE;IACpE,KAAK,EAAE,aAAa,CAAA;IACpB,0DAA0D;IAC1D,aAAa,EAAE,OAAO,CAAA;CACvB;AAED,6BAA6B;AAC7B,MAAM,WAAW,cAAc;IAC7B,kFAAkF;IAClF,WAAW,EAAE,MAAM,CAAA;IACnB,yEAAyE;IACzE,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,eAAO,MAAM,qBAAqB,EAAE,cAAuD,CAAA;AAE3F;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,GAAG,YAAY,CAUpE;AAED,yDAAyD;AACzD,wBAAgB,KAAK,CAAC,SAAS,EAAE,YAAY,GAAG,MAAM,CAarD;AAED;;;GAGG;AACH,qBAAa,eAAe;IAWxB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAVtB,OAAO,CAAC,KAAK,CAAwB;IACrC,OAAO,CAAC,IAAI,CAAoB;IAChC,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,QAAQ,CAAoB;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IAEvC,YACE,MAAM,GAAE,OAAO,CAAC,cAAc,CAAyB,EACtC,GAAG,GAAE,MAAM,MAAiB,EAG9C;IAED,6CAA6C;IAC7C,gBAAgB,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAM3C;IAED,oEAAoE;IACpE,eAAe,IAAI,IAAI,CAEtB;IAED,sDAAsD;IACtD,iBAAiB,IAAI,IAAI,CAOxB;IAED,6CAA6C;IAC7C,MAAM,IAAI,gBAAgB,CAqBzB;CACF"}
@@ -9,14 +9,14 @@
9
9
  * function of (input, nowMs); persistence and RPC live in the service.
10
10
  * @module @linxin666/dsh-pet/state
11
11
  */
12
- export const defaultPetStateConfig = { celebrateMs: 2400 };
12
+ export const defaultPetStateConfig = { celebrateMs: 2400, failureMs: 2400 };
13
13
  /**
14
14
  * Map one activity phase onto the animation contract.
15
15
  * - thinking → `running` and tool → `running-right` (focused work).
16
16
  * - review → `review` while answer text is streaming.
17
17
  * - waiting → `waiting` (expectant pose, needs user input).
18
18
  * - done → `jumping` (celebration), then back to `idle` after the window.
19
- * - failed → `failed` until another activity event arrives.
19
+ * - failed → `failed` briefly, then back to `idle`.
20
20
  * - idle → `idle` (calm breathing loop).
21
21
  */
22
22
  export function animationForPhase(phase) {
@@ -47,27 +47,28 @@ export function rowOf(animation) {
47
47
  }
48
48
  /**
49
49
  * PetStateMachine — one instance per host process. Holds only the latest
50
- * input snapshot and the celebration timing; no storage, no side effects.
50
+ * input snapshot and terminal-state timing; no storage, no side effects.
51
51
  */
52
52
  export class PetStateMachine {
53
- config;
54
53
  now;
55
54
  phase = 'idle';
56
55
  line;
57
56
  phrase;
58
57
  sessionActive = false;
59
58
  doneAt;
59
+ failedAt;
60
+ config;
60
61
  constructor(config = defaultPetStateConfig, now = Date.now) {
61
- this.config = config;
62
62
  this.now = now;
63
+ this.config = { ...defaultPetStateConfig, ...config };
63
64
  }
64
65
  /** Consume one projected activity update. */
65
66
  onActivityStatus(input) {
66
67
  this.phase = input.phase;
67
68
  this.line = input.line;
68
69
  this.phrase = input.phrase;
69
- if (input.phase === 'done')
70
- this.doneAt = this.now();
70
+ this.doneAt = input.phase === 'done' ? this.now() : undefined;
71
+ this.failedAt = input.phase === 'failed' ? this.now() : undefined;
71
72
  }
72
73
  /** A session became the active one (or a fresh session started). */
73
74
  onSessionActive() {
@@ -80,24 +81,23 @@ export class PetStateMachine {
80
81
  this.line = undefined;
81
82
  this.phrase = undefined;
82
83
  this.doneAt = undefined;
84
+ this.failedAt = undefined;
83
85
  }
84
86
  /** Render the current animation decision. */
85
87
  render() {
86
88
  const nowMs = this.now();
87
89
  let animation = animationForPhase(this.phase);
88
- // Celebration window: after `done`, jump for celebrateMs then settle idle.
89
- if (this.phase === 'done' && this.doneAt !== undefined) {
90
- if (nowMs - this.doneAt < this.config.celebrateMs) {
91
- animation = 'jumping';
92
- }
93
- else {
94
- animation = 'idle';
95
- }
96
- }
97
- // Settled sessions never bubble: idle (e.g. an aborted/stopped turn)
98
- // and done past its celebration window both fall silent.
99
- const settled = this.phase === 'idle'
100
- || (this.phase === 'done' && this.doneAt !== undefined && nowMs - this.doneAt >= this.config.celebrateMs);
90
+ const doneSettled = this.phase === 'done'
91
+ && this.doneAt !== undefined
92
+ && nowMs - this.doneAt >= this.config.celebrateMs;
93
+ const failedSettled = this.phase === 'failed'
94
+ && this.failedAt !== undefined
95
+ && nowMs - this.failedAt >= this.config.failureMs;
96
+ if (doneSettled || failedSettled)
97
+ animation = 'idle';
98
+ // Settled sessions never bubble: idle (e.g. an aborted/stopped turn),
99
+ // completed celebration expiry, and failed display expiry all fall silent.
100
+ const settled = this.phase === 'idle' || doneSettled || failedSettled;
101
101
  const bubble = settled ? undefined : this.phrase ?? this.line;
102
102
  return {
103
103
  animation,