feat(react)!: harden lifecycle and expand agent APIs - #23
Conversation
BREAKING CHANGE: AgentMode now includes "thinking" and public context and hook result types add required members.
dg-coreylweathers
left a comment
There was a problem hiding this comment.
DevRel review — changes requested, for completeness of the 0.2.0 release rather than code. Gates, semver mechanics, and the live Voice Agent run all pass; the four items under "Should fix before 0.2.0 ships" are docs and changelog gaps on a breaking release, and the four questions at the end need answers before I sign off.
What this PR does
Moves @deepgram/react onto @deepgram/agents 0.1.2 / @deepgram/sdk 5.9.0 and exposes the SDK's newer surface through the provider, the focused hooks, and useDeepgramAgent: a "thinking" mode, sendAgentMessage, updateListen/Think/Speak/Prompt, and seven typed notification callbacks. It also rewrites start/stop/reconnect/StrictMode handling with generation counters so a cancelled start, a microphone failure, or a dropped session cannot leave stale audio running or send a client-tool result into a replacement session, and removes release-as so Release Please proposes 0.2.0 from the feat! commit.
What I checked
- Do the gates pass with the lockfile frozen? Yes:
bun install --frozen-lockfile, Biome lint (0 diagnostics),tscagainst the npm@deepgram/agents0.1.2 with no../agentcheckout, 64/64 tests,vite build+npm pack(7 files), all in a cleannode:24container. - Does every type, event, and method this PR re-exports exist in the published agents 0.1.2? Yes; checked its
dist/index.d.ts.fn.argumentsis the real field onFunctionCallItemin sdk 5.9.0. - Is the semver story honest? Yes. The built
index.d.tsdiff shows exactly the declared breaks (AgentModegains"thinking"; required members added toAgentContextValueand the hook result types;registerClientToolnow returns an unsubscribe function). No export, prop, or default was removed. Two behavior changes are not declared anywhere:useDeepgramAgent().start()now emptiesconversation, andmicrophone/ttsprop changes now take effect mid-session. - Does it work against the live Voice Agent API? Yes. I drove the built provider in Node with
microphone={false} tts={false}: connect in 180 ms, SettingsApplied ->listening,sendUserMessage-> user + assistant entries,updatePrompt-> PromptUpdated,updateListen->onListenUpdated,sendAgentMessage(..., "interrupt")-> injected assistant turn, a live FunctionCallRequest ->onFunctionCall-> reply "4711.", aregisterClientTooloverride -> reply "9090." with the prop handler never called, 16 latency reports, 0 Error/Warning/sdk-error,stop()->disconnected/idle, restart -> connected withconversationcleared. The standalone hook connected live, then the microphone failure rolled the session back todisconnected/micActive=false/mode="idle". - What did not match the README? For a text-injected turn the server sent no
AgentThinkingand noAgentStartedSpeaking, and withtts={false}the provider skips the audio-basedspeakinginference, somodestayedlisteningthrough the whole reply (97 audio chunks). Same as 0.1.0, but this PR is the one documenting four modes and addingisThinking. - Not live-verified:
tts={true}playback, microphone capture, mode transitions on voice turns, StrictMode replay, and reconnect after a dropped socket (browser audio APIs are unavailable in Node; the mocked unit tests cover these).
Should fix before 0.2.0 ships
- S1
packages/react/README.md"Mode Tracking": state thatspeakingis inferred from incoming agent audio and requirestts={true}, and thatthinkingis set only when the server sendsAgentThinking(not observed onsendUserMessagetext turns). - S2 The generated 0.2.0 changelog will be one Features line plus the BREAKING CHANGE footer. Edit the Release Please PR (or the squash-commit body) to list each new API, each lifecycle fix, and the full breaking list including the
registerClientToolreturn type anduseDeepgramAgent().start()clearingconversation. - S3
onListenUpdatedexists butonPromptUpdated/onSpeakUpdated/onThinkUpdateddo not, althoughupdatePrompt/Speak/Thinkand their message types are exported. Either add the three callbacks (additive) or document that confirmations come fromuseAgentSession().on(...). - S4 Document in the standalone-hook section that
start()begins a fresh session and clearsconversation.
Nits
- README.md:56
MicrophoneOptionshas no VAD option; list the real fields (sampleRate, echoCancellation, noiseSuppression, autoGainControl). - README.md:123 name the
behaviorvalues:"default" | "queue" | "interrupt". - README.md:62
onSdkErroralso receives microphone start failures after connect (provider.tsx:365, :381). - hooks/useAgentSession.ts:6 JSDoc still lists
updatePromptas an escape-hatch use; it is now onuseAgentControls. - ci.yml:19 / npm-publish.yml:45
bun installis unfrozen; add--frozen-lockfile(it passes). - README.md:86-93 / CONTRIBUTING.md:8 the
../agentsibling reads as required; tsconfigpathsfalls back to npm when it is absent, so mark it optional. - package.json:54 TypeScript pinned back 6.0.2 -> 5.9.3 with no stated reason; Dependabot will propose 6.x again (PR #9 already did).
Questions
- Where does the "independent DevRel re-review: approve" in the PR body live? Nothing is on GitHub and I could not find a Slack thread.
- Is "no
AgentThinking/AgentStartedSpeakingon text-injected turns" expected server behavior? It decides the exact S1 wording. - Add the three
*Updatedcallbacks in this release, or defer? - Why the TypeScript downgrade?
|
@dg-coreylweathers Re-review requested. Addressed the should-fix items and nits:
A fresh independent DevRel review found and we fixed auto-start connection failures not reaching |
dg-coreylweathers
left a comment
There was a problem hiding this comment.
DevRel re-review — changes requested, for one new finding, not for anything from the 09-04 list. Every should-fix (S1–S4) and every nit from 09-04 is addressed in 42cc340; S2 is partial only because the changelog handoff does not yet mention the 4525294 behavior change. The blocker is in the type declarations the package publishes, and it predates this PR.
What this PR does
Moves @deepgram/react onto @deepgram/agents 0.1.2 / @deepgram/sdk 5.9.0 and exposes the newer surface (a "thinking" mode, sendAgentMessage, updateListen/Think/Speak/Prompt, typed notification callbacks — now including onPromptUpdated, onSpeakUpdated, onThinkUpdated), and rewrites start/stop/reconnect/StrictMode handling with generation counters. 4525294 routes a failure during autoStart to onSdkError instead of the console.
What I checked
- Do the 09-04 items land? Yes. README now says
speakingis inferred from incoming audio only withtts={true}andthinkingonly when the server sendsAgentThinking(matches the code at provider.tsx:414-417 and :447-449 and what I saw live on 09-04); standalonestart()clearingconversationis documented; the three callbacks are on bothAgentProviderPropsandUseDeepgramAgentOptionsin the builtindex.d.ts;MicrophoneOptionsfields andbehaviorvalues match agents 0.1.2 / sdk 5.9.0; CI and publish use--frozen-lockfile; the sibling checkout reads as optional; the TypeScript 5.9.3 pin is explained and API Extractor 7.58.7 really does pin 5.9.3; Dependabot ignores TypeScript majors. - Do the gates pass? Yes: frozen install, lint, typecheck, 66/66 tests, build, in a
oven/bun:1.3.13container. The two new tests are the standalone-hook callback test and the auto-start failure test. - Does
4525294keep a cancelled start from touching a replacement session? Yes. I ran four extra tests: a cancelled auto-start whose connect later fails never callsonSdkErrorand never disconnects the replacement; the same after unmount; under StrictMode it fires exactly once; a manualstart()failure comes back as a rejected promise and not throughonSdkError. - Does the published package carry the types it advertises? No — see the blocker.
What to fix
- [B1] Published type declarations import
@deepgram/agentstypes from a path that exists only next to a maintainer's checkout.dist/index.d.tshas 42 imports of'../../../../agent/packages/sdk/dist/index'instead of'@deepgram/agents', because vite-plugin-dts rewrites the tsconfigpathsmapping into relative imports by default. Expected: a developer installing the package getsconfigtyped asAgentSessionConfigand each callback's message typed. Observed in a clean-room consumer: withskipLibCheckon (the Vite default)const a: ListenUpdatedMessage = 42compiles — every agent-derived type has becomeany; with it off, sixTS2307: Cannot find moduleerrors insidenode_modules/@deepgram/react. The 0.1.0 tarball on npm already has 16 of these, so this is not new to the PR, but 0.2.0 would ship 42. Fix, verified by rebuilding:packages/react/vite.config.ts:25→dts({ rollupTypes: true, pathsToAliases: false })gives 0 sibling paths and 33from '@deepgram/agents'imports (grep -c "agent/packages/sdk" packages/react/dist/index.d.tsprints 0). Please confirm the same output on a machine where../agentis present, and add a changelog line for it. - [S5] The auto-start test rejects
connect(), but agents 0.1.2 never rejectsconnect()on a connection failure (it emitssdk-errorand reconnects), so the path that actually reaches the new catch is a microphone failure during auto-start. Add a case withfailNextMicrophoneStart+autoStartasserting oneonSdkErrorcall and the rolled-back state. - [N1] README:67 — add that a manual
start()call rejects instead of callingonSdkError. - [N2] Changelog handoff: add "Report auto-start failures (for example, microphone permission denied) through
onSdkErrorinstead ofconsole.error." under Lifecycle fixes.
Questions
- Is the absence of
AgentThinking/AgentStartedSpeakingon text-injected turns expected server behavior? (Carried from 09-04; the README hedge is fine either way.) - The comment says a fresh independent DevRel review returned Approve with no findings — where does it live? Nothing on GitHub carries it.
dg-coreylweathers
left a comment
There was a problem hiding this comment.
DevRel re-review — approving. Every item from the 09-07 round is fixed and verified; three optional nits remain.
What this PR does
Moves @deepgram/react onto @deepgram/agents 0.1.2 / @deepgram/sdk 5.9.0, exposes the newer surface (a "thinking" mode, sendAgentMessage, updateListen/Think/Speak/Prompt, typed notification callbacks including onPromptUpdated / onSpeakUpdated / onThinkUpdated), and rewrites start/stop/reconnect/StrictMode handling with generation counters. The six commits since 09-07 make the published type declarations portable, settle what happens when a start fails, and document it.
What I checked
- Do consumers get real types now? Yes. The built
dist/index.d.tshas zero imports from the../agentsibling path and 33 from@deepgram/agents. In a clean-room project (packed tarball, agents 0.1.2, TypeScript 5.9.3) assigning42to aListenUpdatedmessage or12345toconfignow fails with the real type names, and there are zero "Cannot find module" errors from this package withskipLibCheckoff. On 09-07 the same test compiled silently (every borrowed type wasany) and produced six such errors. - Was there a double
onSdkErrorcall? Yes, and it is fixed. At the previous head a microphone failure duringautoStartcalledonSdkErrortwice — once from theconnectedhandler, once fromstart()'s rethrow. The microphone-failure test asked for on 09-07 is what caught it;d0309c4adds an in-progress guard. I ran seven extra lifecycle tests: a cancelled auto-start never reports or touches its replacement; after unmount nothing fires; under StrictMode a failure (connection or microphone) fires exactly once; a manualstart()failure (connection or microphone) rejects, rolls back todisconnected/micActive=false/mode="idle", and never callsonSdkError; a reconnect after a completed start still restarts the microphone. - Does the README describe that contract? Yes for the provider:
onSdkErroris "SDK transport or automatic-start failure" andstart"rejects on failure and does not call onSdkError"; the quickstarts and the example wrapstart()in try/catch. - Gates: frozen install, lint, typecheck, 67/67 tests (was 66), build,
git diff --check, all green in aoven/bun:1.3.13container; CI green. No connect or message code path changed, so no live Voice Agent rerun.
Optional nits
packages/react/README.md:230— the standalonestart()line should carry the same "rejects on failure and does not callonSdkError" note; the hook behaves identically.- Changelog handoff — broaden the Lifecycle line: "Report automatic start failures (auto-start, microphone re-enable, reconnect) through
onSdkError, orconsole.errorwhen no callback is set; manualstart()rejects instead." (76fa954also turns previously silent re-enable/reconnect microphone failures intoconsole.error.) - Reconnect test — restore
expect(microphones).toHaveLength(2)after thewaitForso a third microphone would still fail it.
Still open, not blocking
- Confirm on a machine with
../agentpresent that the build still emitsfrom '@deepgram/agents'(CI builds without the sibling, so the npm artifact is right either way). - Is the absence of
AgentThinking/AgentStartedSpeakingon text-injected turns expected server behavior? - At release time I will paste the four "Release Please Handoff" sections into the release PR's
packages/react/CHANGELOG.md; Release Please copies only the title andBREAKING CHANGE:footer.
Summary
@deepgram/reactwith published@deepgram/agents0.1.2 and@deepgram/sdk5.9Breaking Changes
AgentModenow includes"thinking"; exhaustive consumers must handle the new memberBREAKING CHANGE: AgentMode now includes "thinking" and public context and hook result types add required members.
The breaking conventional title and commit footer intentionally direct Release Please to propose
0.2.0from0.1.0.Lifecycle Safety
sdk-erroris forwarded separately from protocolerrorValidation
bun install --frozen-lockfilebun run lintbun run typecheckbun run test(64 tests)bun run buildRelease Please Handoff
When Release Please opens the 0.2.0 release PR, edit packages/react/CHANGELOG.md to include these release notes:
New APIs
thinkingmode andisThinkingto mode state.sendAgentMessagewithdefault,queue, andinterruptbehavior.updateListen,updateThink,updateSpeak, andupdatePrompt, plus matching update-confirmation callbacks.Lifecycle fixes
Fixes
Breaking changes
AgentModeadds"thinking"; exhaustive consumers must handle it.AgentContextValueand public hook results add required state and control members.registerClientToolnow returns an unsubscribe function.useDeepgramAgent().start()begins a fresh session and clearsconversation.Corey’s review is the source of the current required follow-ups.