feat(webchat): lobster pet sentience — gaze, petting, honest run reactions, vigil#103344
Conversation
…s, vigil The pet watches your pointer between acts (facing follows the cursor, throttled, reduced-motion inert). Press-and-hold now pets it - content closed eyes and a floating heart - while a quick tap stays a poke. Run endings are honest: resolveLobsterRunOutcome picks the most recently ended session (endedAt first, activity stamps as fallback) so successes cheer, failures earn a sympathetic droop with sagging antennae, and user-aborted runs just get an acknowledging startle. Runs longer than ten minutes summon the pet into a settled vigil pose that pauses all acts until the run ends.
64ef25c to
49124b8
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 49124b8f43
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| this.vigilTimer = null; | ||
| } | ||
| if (this.mode === "busy") { | ||
| this.vigilTimer = window.setTimeout(() => { |
There was a problem hiding this comment.
Base vigil timing on the run start
When the sidebar mounts or reconnects while a run is already older than ten minutes, this always starts a fresh 600s timer from the current render; app-sidebar only passes mode/runOutcome, even though session rows carry startedAt, so an already-long run can wait another full ten minutes before the vigil appears. Use the active run's elapsed time to arm immediately or with only the remaining delay.
Useful? React with 👍 / 👎.
| this.vigil = false; | ||
| this.trackVigil(); | ||
| } else if (changed.has("mode")) { | ||
| this.trackVigil(); |
There was a problem hiding this comment.
Preserve the finish act after vigil
When the pet is visible only because vigil was active, a busy→idle transition calls trackVigil() first, clearing this.vigil; after performAct() sets the cheer/droop/startle, reconcilePresence() sees wantsVisible() as false and clears the act while starting departure, so long-run failures/successes just leave instead of showing the honest finish reaction. Keep the vigil presence until the finish act completes, or defer clearing it until after that reaction is visible.
Useful? React with 👍 / 👎.
|
Merged via squash.
|
…s, vigil (openclaw#103344) The pet watches your pointer between acts (facing follows the cursor, throttled, reduced-motion inert). Press-and-hold now pets it - content closed eyes and a floating heart - while a quick tap stays a poke. Run endings are honest: resolveLobsterRunOutcome picks the most recently ended session (endedAt first, activity stamps as fallback) so successes cheer, failures earn a sympathetic droop with sagging antennae, and user-aborted runs just get an acknowledging startle. Runs longer than ten minutes summon the pet into a settled vigil pose that pauses all acts until the run ends.
Related: #102754 (lobster pet series, round 3)
What Problem This Solves
The pet reacts to status but not to you, and its reactions are dishonest at the edges: a failed or user-aborted run currently earns the same cheer as a success.
Why This Change Was Made
Four sentience behaviors, element-scoped:
resolveLobsterRunOutcomepicks the most recently ended session (endedAtfirst — activity stamps move on unrelated events — with fallbacks) and the busy→idle reaction now branches: success cheers, failure/timeout earns a sympathetic droop (sagging antennae, slow sad head-shake), and a user-killed run gets a plain acknowledging startle rather than a celebration.User Impact
The lobster looks at your cursor, can be petted, reacts truthfully to how your run actually ended, and sits with you through the long ones. No config changes; reduced-motion stays fully inert on every new path.
Evidence
Validation (delegated Blacksmith Testbox,
exit=0):pnpm test ui/src/components/lobster-pet.test.ts— 33/33: outcome resolver units (latest-ended ordering, endedAt precedence, aborted classification), hold-pets vs tap-pokes without double-fire, droop on failed runs, vigil arming from the initial busy render + act pause + release, pointer-gaze facing flips, plus the full existing suite.pnpm tsgo:test:uiandpnpm --dir ui build.pnpm dev:ui:mock: gaze both directions, hold-pet with heart animation, droop on error outcome, vigil class.endedAt, and killed runs being celebrated; final review clean.