Commit Graph
161 Commits
Author SHA1 Message Date
snowspeeder 6cb4427210 feat: save account model and thinking settings for Hermes
Rebuild & Deploy / rebuild (push) Successful in 1m9s
2026-10-01 16:38:34 -04:00
snowspeeder a45569f8c1 fix: verify deployment health in Docker host network
Rebuild & Deploy / rebuild (push) Successful in 44s
2026-10-01 16:22:04 -04:00
snowspeeder 3122ca7605 fix: validate host certificates through Docker in CI
Rebuild & Deploy / rebuild (push) Failing after 1m9s
2026-10-01 16:19:58 -04:00
snowspeeder 129159ffd1 fix: support legacy Docker builder in deployment
Rebuild & Deploy / rebuild (push) Failing after 1m12s
2026-10-01 16:16:47 -04:00
snowspeeder c5f7bb39e7 fix: delay and vary spoken wait notices
Rebuild & Deploy / rebuild (push) Failing after 7s
2026-10-01 15:59:10 -04:00
snowspeeder 6f2659421c docs: record signed iPhone installation and successful launch 2026-10-01 15:22:00 -04:00
snowspeeder 9cc6147c6f test: verify rollback restores originals on partial deployment failure
Rebuild & Deploy / rebuild (push) Failing after 15s
2026-10-01 13:40:21 -04:00
snowspeeder df25c7336c docs: record live speech qualification and secure operator handoff 2026-10-01 13:38:01 -04:00
snowspeeder 27853f7c27 fix: restore original services on partial deployment failure 2026-10-01 13:34:20 -04:00
snowspeeder 2aedea84b5 feat: provision private Hermes assistants for authenticated Hope accounts 2026-10-01 13:30:18 -04:00
snowspeeder bebaf59466 feat: authorize iOS Hermes accounts and secure session isolation 2026-10-01 12:57:57 -04:00
snowspeeder 9bdaea2408 Document engineering learnings and refresh handoff guidance 2026-09-19 14:40:24 -04:00
snowspeeder 85169d651c project: regenerate to include asset catalog resource phase 2026-09-12 17:27:00 -04:00
snowspeeder 7ac8a1c2fa brand: add Hope app icon (Lightwell aperture)
- 1024px icon from web/brand/hope-icon.svg (forced-dark, grid-tuned)
- fix xcodegen: xcassets was never in the generated project (no
  resources phase) — move it into sources with buildPhase: resources
- fix iconset manifest: idiom universal (universal-platform rejected
  by actool), single 1024 universal/ios entry
2026-09-12 17:26:57 -04:00
snowspeeder da65da68b3 brand: adopt Lightwell aperture as the Hope brand mark
- web/brand/hope-mark.svg: canonical adaptive mark (light/dark)
- web/brand/hope-logo-horizontal.svg: lockup with rounded wordmark
- web/brand/hope-icon.svg: forced-dark icon-grid-tuned source
- favicon.svg: small-size tuned aperture
- manifest + index.html: PNG icons (192/512), apple-touch-icon, theme #1C191F
2026-09-12 17:26:54 -04:00
snowspeeder 59d8182b6c docs: record final device install 2026-09-12 17:13:27 -04:00
snowspeeder b7cec80a99 fix: close Nearlight release blockers 2026-09-12 10:13:14 -04:00
snowspeeder 5577162594 Reapply N2 fix: disable Send until transport ready (lost in prior commit) 2026-09-12 08:38:08 -04:00
snowspeeder f6722a223f Fix regressions N1-N3 from re-verification
- Transcript: selection suspension clears when press ends; Latest
  re-enables follow fully (clears stale selection state)
- Send disabled until transport ready in pending Type composer
- Server URL: programmatic success-trim no longer erases its own
  saved-confirmation; only user edits clear it
2026-09-12 08:36:01 -04:00
snowspeeder afe37ad5f4 Fix reading-safety and accessibility majors from review
- Transcript: drag suspends auto-follow; long-press selection suspends
  following; Latest is a trailing outlined capsule that re-enables follow
- Lightwell: pulse via isolated TimelineView (2.4s triangle wave), pauses
  when offscreen; Reduce Motion uses static geometry (no level resizing)
- Increase Contrast: solid Rosewood lightwell fill, 2pt Rosewood selected
  outlines on mode buttons
- Haptics: success only on entering ready, error only on entering failure
2026-09-12 08:27:48 -04:00
snowspeeder f043d6b078 Fix major Nearlight deviations from design review
- Expose End conversation + readable draft during connecting/waiting
- Stop masking assistant speaking/thinking state in Type/muted modes
- Restrict failure diagnosis to real causes (no invented network text)
- Replace old blue tint with Rosewood everywhere
- Fix transcript short-content bottom-anchor (top-align short histories)
- Settings: visible Server URL label, focus on rejected save, edit clears
  saved confirmation, trim-aware unchanged detection
- Task detail: live store lookup with missing-task state
- Task rows: status text label restored
2026-09-12 08:20:43 -04:00
snowspeeder fa21c81787 wip: Nearlight redesign implementation 2026-09-12 08:14:06 -04:00
snowspeeder c76afbd11e docs: record verified device pairing, signing, install 2026-09-12 07:17:55 -04:00
snowspeeder b01ee6478d fix: background tasks follow the live model selection
TaskWorker resolved its model from LLM_MODEL env (frozen at MCP spawn),
so after a live set_model switch in the UI, dispatched background tasks
still asked for the old model. On local-vl-models (llama-swap
--models-max 1) that forces a swap against the loaded conversation
model; a force-kill mid-load surfaces as 400 'model failed to load'.

The agent now writes the selected model to <task_dir>/model.txt at
session start and on every switch; TaskWorker reads it with the env
value as fallback.
2026-09-12 07:16:08 -04:00
snowspeeder ea9ec75631 Configure development team for device signing 2026-09-12 07:10:41 -04:00
snowspeeder c5db94658d Restore same-URL guard in replaceServer
Accidentally dropped during the Music tab removal edit; without it,
reapplying the current URL recreates the session and loses history.
2026-09-12 06:57:32 -04:00
snowspeeder 436b3fc8b1 refactor: split music streaming into Hope Music
Rebuild & Deploy / rebuild (push) Successful in 2m7s
Move the media server, downloader, library volume, and browser player to the
standalone hope-media-music product. Keep Hope's music tools as a thin HTTP
client of the LAN service on port 8094.

Use a stable voice-room player scope instead of ephemeral LiveKit room names.
The agent now owns the conversation handshake: fresh reported playback mutes
its input, joining pauses music, and the last participant leaving resumes only
music it paused. Peer failures remain non-fatal.

Also move the voice deploy workflow onto the active Unraid Gitea runner and
remove yt-dlp/ffmpeg/media health and volume dependencies from the image.
2026-09-12 06:35:15 -04:00
snowspeeder ed329213de Remove embedded music player
The music feature is retired: delete the Media tab, MediaAPI,
MediaPlayerController, PinnedMediaResourceLoader, MediaPanelView, the
media_state control publish, media scope handling in VoiceSession, the
volume setting, and all media tests. App is now Talk + Activity tabs.

Backend media API in hope-voice-api is untouched (still serves the web UI).
2026-09-12 06:26:58 -04:00
snowspeeder 3620a2387b docs: update repo URL after rename to hope-voice-ios 2026-09-12 06:11:39 -04:00
snowspeeder 24f95639dc Redesign Hope iOS UI, harden playback, and document project handoff 2026-09-09 20:23:44 -04:00
snowspeeder 40aaf4fea7 status 2026-08-31 19:50:46 -04:00
snowspeeder c9c25f86eb docs: record Phase 9 commit hash in PLAN.md 2026-08-30 09:41:10 -04:00
snowspeeder e63e53ee74 docs: README + device testing checklist (Phase 9) 2026-08-30 09:41:04 -04:00
snowspeeder 12ded179eb feat: hardening, error states, interruption handling, a11y (Phase 8) 2026-08-30 09:37:05 -04:00
snowspeeder 9f1f26ba54 docs: record Phase 7 commit hash in PLAN.md 2026-08-30 08:48:38 -04:00
snowspeeder af296da6b8 feat: background tasks panel (Phase 7) 2026-08-30 08:48:33 -04:00
snowspeeder 5c35fa9c65 docs: record Phase 6 commit hash in PLAN.md 2026-08-30 08:40:03 -04:00
snowspeeder cbf18306ff feat: media search, choices, library management (Phase 6) 2026-08-30 08:39:57 -04:00
snowspeeder c10ea72cd6 docs: record Phase 5 commit hash in PLAN.md 2026-08-30 08:21:16 -04:00
snowspeeder 0af944e60d feat: media player with reconciliation and pinned media proxy (Phase 5)
- MediaAPI client for all 10 media endpoints + Codable models
- PinnedMediaResourceLoader: AVAssetResourceLoaderDelegate proxying
  https media through the pinned-TLS session (AVPlayer ignores registered
  URLProtocol subclasses and can't trust the self-signed cert)
- MediaPlayerController: AVPlayer + 1.5s poll reconciliation ported from
  web app.js (track swap, late audio_url, desired state, seek_hint,
  reporting, enter-media-scope carry-over, post-talk resume)
- MediaPanelView UI + music.note toolbar entry
- VoiceSession wiring (enterMediaScope/endConversation)

Playback of the library's webm tracks is blocked by an iOS platform
limitation (AVPlayer cannot decode webm); TLS/Range proxy, reconciliation,
polling, reporting and UI are all verified working in the simulator.
2026-08-30 08:21:08 -04:00
snowspeeder f2022f8a96 docs: mark Phase 4 done with commit hash 2026-08-30 07:50:50 -04:00
snowspeeder ca2d26f171 feat: voice and model pickers with sync-on-join (Phase 4)
- Voices.swift: 13 Azure voices, default en-US-Ava
- SettingsStore: persist voiceID/modelID, fallback to default voice
- VoiceSession: sync set_voice/set_model on agent_ready, selectVoice/selectModel, track models + model_changed
- SettingsView: voice and model picker sheets
- ControlChannel: publish via publish(data:) reliable data channel instead of sendText DataStream (agent listens on legacy data_received; DataStream text was dropped)
2026-08-30 07:50:45 -04:00
snowspeeder 5483e6ed48 feat: conversation state machine, mic meter, history, wake lock (Phase 3) 2026-08-30 07:36:13 -04:00
snowspeeder 4986b6d7dc feat: LiveKit voice session, control channel, transcripts (Phase 2)
- VoiceSession: token->connect with fresh-room retry on timeout, agent
  discovery, agent_ready wait, mic enable/mute, text send over lk.chat
- ControlChannel: outbound set_voice/set_model/get_models/set_mode/
  media_state; inbound agent_ready/models/model_changed/task_event
- TranscriptHandler: transcription segments + transcript topic finals,
  partial/final dedupe per role
- ConversationView/OrbView: status orb, mic/text-mode toggles, composer
- ServerConfig: plain ws:// signaling for private IPs (self-signed TLS
  not injectable into SDK WebSocket); Info.plist local networking
2026-08-30 07:26:58 -04:00
snowspeeder d4ea4fc984 fix: background tasks now fetch real page content; add task-status tools
Rebuild & Deploy / rebuild (push) Successful in 58s
Background research tasks were producing garbage or failing because the
self-hosted Firecrawl /v1/scrape defaults to a ~30s timeout that's too short
for heavy news/research sites, and the small LLM won't reliably chain a
second scrape call after a search that only returns snippets.

task_worker.py:
- web_search now auto-fetches the top results directly from the container
  (which has working outbound internet) and returns their real text, so the
  model gets actual page content in one call instead of snippet echoes.
- web_scrape requests timeout: 90000 (Firecrawl renders heavy sites but needs
  >30s), and falls back to a direct container fetch with tag-stripping if
  Firecrawl times out or returns nothing.

task_registry.py / dispatch_mcp.py:
- Add task_status(id) and list_tasks() tools so Hope can check the live state
  of any dispatched task (pending / running / completed / failed / STALLED).
- Track last_activity per task and surface a stall heuristic.
- Move the status summary into task_registry (no mcp dependency) so it's
  unit-testable.

agent.py: prompt tells Hope about task_status / list_tasks.
tests: add task-status summary and last-activity regression tests.
2026-08-30 06:54:57 -04:00
snowspeeder df8aa64504 fix: start cached songs even when played by voice (autoplay + room scope)
Rebuild & Deploy / rebuild (push) Successful in 50s
Hope often starts an already-downloaded song from a voice command, with no
click on the player. Two things could leave it paused at 0:

- The <audio> element sits outside the LiveKit room's audio-unlock flow, so
  autoplay policy rejected the poll-driven play(). Capture the first page
  gesture (pointerdown/keydown/touchstart) and play the current src inside it
  to whitelist the element for autoplay.
- Player state is scoped per LiveKit room, but mediaScope() could return a
  stale shell room after a reconnect. Derive it from the connected room.name
  so the browser polls exactly the room the agent joined, and a "play X"
  request can never land in a scope the browser isn't watching.

Add an in-container regression test (test_media_play_resolves_cached_ready_track)
pinning the server-side resolve: an already-ready track plays without a
re-download and serves a stable audio_url.
2026-08-29 17:37:51 -04:00
snowspeeder 436a26a0fc feat: show album art in the avatar square while music is loaded
The LED wall square switches to the track's cover art whenever a song is
loaded outside a conversation, and returns to the pulsing LED wall while
speaking to the agent (music and conversation are mutually exclusive, so
starting a talk hides the art and ending it restores it).
2026-08-29 17:37:39 -04:00
snowspeeder 378b8cb36f feat: server config, token client, TLS pinning, connection test (Phase 1)
ServerConfig parses https base URL and derives wss livekit URL;
TokenClient POSTs /token with clean 503/offline error states;
TLSPinning holds optional cert SHA-384 pins (empty = system trust);
Settings gains a Connection test screen proving the endpoint works.
2026-08-29 17:18:38 -04:00
snowspeeder 297fbaa19c chore: scaffold Voice iOS app (Phase 0)
XcodeGen project, SwiftUI iOS 17 target dev.shane.voice, SPM
livekit-client-swift 2.16.0, mic usage + ATS LAN exception in
Info.plist, placeholder conversation screen and Settings with
server URL field.
2026-08-29 17:15:27 -04:00
snowspeeder 68f4c461c8 feat: replace orb avatar with a livekit-style LED wall driven by agent audio
Rebuild & Deploy / rebuild (push) Successful in 37s
- Swap the CSS orb for a 21x21 LED dot matrix on a glass panel, matching
  the livekit.com LED-array hero visual.
- Pulse driven by the agent's rendered audio: an expanding radial bloom
  brightens and widens with remote-audio level, dimmer when listening on
  the mic, faint static matrix when idle, and a subtle warm breathing
  state while thinking.
- State-tinted palettes: cool azure when speaking, blue when listening,
  amber when thinking, dim slate when idle.
- Drop the unused orb CSS-variable pulse plumbing and the presence-breathe
  / orbit-turn keyframes; the wall renders per-frame from the analyser.
2026-08-29 17:05:57 -04:00