Skip to content

Releases: SmooAI/chat-widget

v0.16.5

Choose a tag to compare

@brentrager brentrager released this 26 Aug 02:31
1f84df2

Patch Changes

  • eb336f4: Fix a returning visitor with a dead session pointer getting a brand-new conversation (SMOODEV-3057)

    connect() probed /internal/resume-by-fingerprint only when there was NO
    persisted sessionId. A persisted-but-dead pointer cleared itself and went
    straight to create_conversation_session, skipping the probe — so a visitor
    whose stored session had ended minted a fresh conversation even when a resumable
    one existed, and one visitor showed up as several inbox rows. The dead-pointer
    path now falls through to the same probe.

    Also makes the probe legible. It swallowed every failure in a bare catch {}
    and { resumable: false } carried no reason, so "no prior visit", "blocked by
    the CRM link", "session ended" and "the lookup 500'd" were indistinguishable —
    which is why this went undiagnosed. The probe now always yields a reason (the
    server's when the response carries one, a derived label when it does not),
    logs it at console.debug, and exposes it as
    ConversationController.lastResumeReason. Failures stay non-fatal: a resume
    probe still never breaks connect().

    Dead-session recovery (send() → recreateSession()) now runs the same probe
    before minting. A session id the pod's registry has lost is often still alive in
    storage, and the wrapper reads storage — so the visitor's conversation is
    recovered instead of splitting in two mid-chat. Bounded as before: send()
    retries this path exactly once.

    Every create_conversation_session now carries a resumeDiagnostics block in
    its metadata — storage (durable/memory), pointer (none/dead/recovery) and
    probe (the resume reason). metadata_json is persisted on the session row, so
    the next duplicate pair can be explained from Postgres rather than from a
    browser console nobody was watching. storage: 'memory' is the notable one: a
    sandboxed iframe or privacy mode silently drops the widget to an in-memory
    store, so it cannot recognise its own previous visit and every page load starts
    from scratch.

v0.16.4

Choose a tag to compare

@brentrager brentrager released this 25 Aug 19:08
da79225

Patch Changes

  • f1048ba: Fix: the widget could report "Online" and then fail every send

    A visitor on smoo.ai completed the pre-chat form, saw the status go "Online", and then got "We couldn't reach the chat." on every turn while the backend was entirely healthy. Three defects, all on the connect path:

    • connect() early-returned while a connect was in flight. The guard was right — one connect, not two — but it returned an already-resolved promise, so every await connect() resolved before a session existed and send() fell through to its "not connected" throw. Four of the six call sites are fire-and-forget void connect() (launcher click, pre-chat submit, full-page mount, voice hand-off), so an awaiting caller racing an in-flight one is the normal case, not an edge. Concurrent callers now share one connect and each await resolves when that connect finishes.
    • A create_conversation_session that resolved without a session id was accepted, assigning undefined and still flipping the status to ready. That is a permanent wedge: every later send hits the missing id, calls connect(), is early-returned by the ready status, and throws again. It now fails honestly and retryably.
    • tryResume() read the session snapshot's status outside its try/catch. The operator client returns an immediate_response's data without checking its status, so an error-status reply carrying no data resolves as undefined rather than rejecting — and the property read threw a TypeError past the catch and out of connect().

    Also hardened: send() now renders the one human error sentence when a connect is genuinely fatal, instead of rejecting out of send() — which dropped the visitor's typed text into an empty transcript and raised an unhandled rejection on the host page. A failure reaching the optional resume probe is recoverable by starting a fresh session; a failure reaching the transport itself stays fatal.

v0.16.3

Choose a tag to compare

@brentrager brentrager released this 24 Aug 19:54
f26426f

Patch Changes

  • f3e48d8: Never render a raw backend error as agent dialogue, and recover from a dead session.

    A visitor on a live marketing site was shown Error: session '<uuid>' not found
    in what looks like an agent chat bubble: send()'s catch rendered
    ProtocolError.message verbatim. That was the only seam where an error frame
    could reach the transcript; every failure now renders one short human sentence
    (connectionErrorMessage), with the machine detail going to the connection-status
    channel instead.

    The turn also had no recovery once the session died mid-conversation — the widget
    wedged and every retry re-sent into the same dead session. A SESSION_NOT_FOUND
    on send now clears the stale pointer (dropping any OTP proof bound to it),
    creates a fresh session on the same socket, and re-sends the turn once, so the
    visitor's message still gets answered. Matched on the protocol code and on that
    code alone: STORAGE_ERROR, INTERNAL_ERROR, auth and rate-limit failures leave
    the live session and its history intact.

v0.16.1

Choose a tag to compare

@brentrager brentrager released this 29 Jul 21:01
7ef46be

Patch Changes

  • a5225bc: Voice: stop the agent's opening greeting from cutting itself off. Barge-in was firing on a single mic frame (~2.67ms) over the RMS threshold, so residual acoustic echo from the greeting (browser AEC is imperfect on speakers), a cough, or a noise blip would interrupt the agent mid-sentence — most visibly, the greeting never finished. Barge-in now requires ~200ms of sustained above-threshold audio (new bargeInMinMs option, default 200); real speech crosses it easily, transient blips don't. The run resets on any quiet frame and on each new agent utterance.

v0.16.0

Choose a tag to compare

@brentrager brentrager released this 18 Jul 03:37
63553f6

Minor Changes

  • 6436692: Voice: speak-and-read mode + seamless voice→text continuity (SMOODEV-2674). A speaker toggle beside the mic lets visitors turn agent speech off — sessions start STT-only (tts:false on the browser-voice start frame; the server skips TTS entirely) while replies still arrive as chat bubbles. New voice.tts config sets the default. Starting voice now connects the text session first, so a voice-first visitor lands in a real conversation that continues seamlessly when they switch back to typing.

v0.15.2

Choose a tag to compare

@brentrager brentrager released this 18 Jul 02:58
19f3ab5

Patch Changes

  • eb7717c: Suggested-reply chips now render inline in the message flow, directly under the latest assistant reply (previously a fixed slot above the composer, visually detached), and the strip is removed the instant a chip is tapped. Chips remain optional shortcuts — the composer stays live.

v0.15.1

Choose a tag to compare

@brentrager brentrager released this 17 Jul 23:40
2480ffc

Patch Changes

  • dc17f25: Fix heavy static on browser-voice TTS playback (SMOODEV-2668): re-align linear16 frames that split mid-sample (an odd-length frame made every later frame decode one byte off — pure noise), resample 16 kHz audio to the AudioContext's native rate with a proper band-limited streaming upsampler instead of forcing a 16 kHz context (whose browser-side output resampling mirrored speech energy above 8 kHz as harsh imaging static), and prime playback ~100 ms off the playhead when the queue drains so just-in-time chunks don't click.

v0.15.0

Choose a tag to compare

@brentrager brentrager released this 15 Jul 21:49
c9baddb

Minor Changes

  • c492c54: Render the new stream_preamble frame: when the operator streams a fast-model preamble ("what I'm about to do") ahead of the answer, show it in the assistant bubble's typing slot (muted + italic) instead of the bare typing dots, then swap in the real answer the moment it starts streaming. A late preamble frame (after the answer began) is ignored. Requires @smooai/smooth-operator ≥ 1.22.15 (adds stream_preamble to the event union so the client forwards it).

v0.14.1

Choose a tag to compare

@brentrager brentrager released this 14 Jul 02:25
4d63d60

Patch Changes

  • db9a07a: Fix a transient double-newline (extra blank line) during streaming. The streamed
    token render and the finalized responseParts render now share one paragraph
    separator and normalizer, so mid-stream no longer shows an extra blank line that
    vanishes when the message finalizes. The finalized output is unchanged for
    well-formed responses.

v0.14.0

Choose a tag to compare

@brentrager brentrager released this 12 Jul 03:12
31d0c6a

Minor Changes

  • 1295007: Voice input/output (SMOODEV-2534): mic capture + TTS playback over the browser-voice WebSocket, shipped dark behind the new voice: { enabled, url? } config option (OFF by default — zero UI when off). Adds VoiceSession (framework-free WS lifecycle, getUserMedia → AudioWorklet/ScriptProcessor → 16 kHz linear16 mic streaming, gapless PCM playback, RMS barge-in with interrupt + playback flush), an Aurora Glass mic toggle in the composer with listening/speaking indicators and a live partial transcript in the input, and voice turns (transcript_final / reply_text) rendered through the normal chat message path. The text session's conversation id is passed as conversation_id so voice resumes the same thread.