13 KiB
Guidelines
- Implement the first available task, top down
- Only do a single task and exit, the other agents will implement the other tasks
- Commit after each task with a descriptive commit message.
- Add context after each task completion, indented under the task, to help the reviewer understand the changes.
Tasks
-
Make sure we are handling edit / read / command tool calls more prominently in the front end, for edits we should show the diff like we do in the git diff screen, for reads we should show the content, and for commands we should show the command output. This would be in the bottom sheet of the tool call. You have to test each agent provider to figure out their specific format, use zod to parse the input/output and show the diff/content/command output accordingly.
- Added zod-backed parsers for edit/read/command tool calls so the bottom sheet now renders diffs, file contents, and terminal output directly from structured tool input/output across providers. Implemented new UI sections plus type-safe helpers, and ran
npm run typecheck --workspace=@voice-dev/app. Manual provider-by-provider verification still needs to run on-device once agents are available.
- Added zod-backed parsers for edit/read/command tool calls so the bottom sheet now renders diffs, file contents, and terminal output directly from structured tool input/output across providers. Implemented new UI sections plus type-safe helpers, and ran
-
Same goes for permission tool calls, we should render more richly the permission prompt in the agent stream, ExitPlanMode from Claude we should render the markdown for example. For edit permissions we should show the diff like we do in the bottom sheet tool call.
- Added shared tool-call parsers plus a reusable diff viewer, then upgraded the permission cards to render plan markdown (ExitPlanMode), shell metadata, diffs, read content, and the raw payload directly inside the stream. Ran
npm run typecheck --workspace=@voice-dev/app; please test across providers on-device once agents are hooked up.
- Added shared tool-call parsers plus a reusable diff viewer, then upgraded the permission cards to render plan markdown (ExitPlanMode), shell metadata, diffs, read content, and the raw payload directly inside the stream. Ran
-
Investigate how each agent provider handles todo lists, we should have first class support for rendering those in the agent stream. I believe the Codex calls them plan and Claude uses TodoWrite tool calls. You can search the web, look at their node modules or just experiment via testing, which is a good diea anyways becaue we want tests for this, you just have to thinka bout how to trigger the agent to do plans / todo lists. Maybe just ask directly.
- Normalized
todotimeline entries into a dedicatedtodo_liststream item, rendered them with a new plan card in the agent stream (provider badge, completion status, checkboxes), and added consolidation logic plus regression coverage intest-idempotent-stream.ts. Rannpm run typecheck --workspace=@voice-dev/app.
- Normalized
-
Change "Refresh from disk" to "Refresh" in the agent three dot menu
- Updated the agent overflow menu label in
packages/app/src/app/agent/[id].tsxso the refresh action now matches the desired wording while keeping the busy state text untouched; no additional changes were required.
- Updated the agent overflow menu label in
-
Are we filtering our own shats (already present in agents storage) from the resume agent list? We should if not.
- Resume tab now filters out persisted sessions whose session ids match any active agent (live session id or persisted handle) to avoid duplicate entries; verified via
npm run typecheck --workspace=@voice-dev/app.
- Resume tab now filters out persisted sessions whose session ids match any active agent (live session id or persisted handle) to avoid duplicate entries; verified via
-
Getting "two children with the same key" for "thoughts" and "assistant" review our keying strategy, and make it more robust and performant, and stable.
- Added deterministic per-entry suffixes when creating assistant and thought timeline ids so FlatList keys remain unique even when providers replay identical text chunks with the same timestamps; reran
npm run typecheck --workspace=@voice-dev/app.
- Added deterministic per-entry suffixes when creating assistant and thought timeline ids so FlatList keys remain unique even when providers replay identical text chunks with the same timestamps; reran
-
Hydrated session show previous tool calls as loading. At least for claude we're not loading the output Chekc Codex too.
- Tool call snapshots now infer completed/failed states when historical events lacked an explicit status, and we accumulate every tool payload in
rawso hydrated sessions expose prior diffs/reads/command output instead of staying in a loading state. Added regression coverage intest-idempotent-stream.tsand rannpm run typecheck --workspace=@voice-dev/app.
- Tool call snapshots now infer completed/failed states when historical events lacked an explicit status, and we accumulate every tool payload in
-
Add agent type indicator in the agent list, so we can quickly identify the agent type (Claude, Codex, etc.). On the left of the status pill.
- Agent cards now include a provider badge left of the status pill by pulling provider labels from the manifest, with new styles to match the sidebar treatment; ran
npm run typecheck --workspace=@voice-dev/app.
- Agent cards now include a provider badge left of the status pill by pulling provider labels from the manifest, with new styles to match the sidebar treatment; ran
-
Hydrated agents still show loading state for tool calls, check this properly, it's not fixed. I am also not seeing the tool call output in the agent stream, which is important.
- Tool snapshots now infer completed/failed states by walking the raw payload (exit codes, tool_result/error flags) when status/result/error are missing, and added regression coverage in
test-idempotent-stream.ts. The agent stream cards now render command output/read content/diff previews inline plus show failures, and we pass result/error data through so hydrated tool calls immediately display their output. Verified withnpm run typecheck --workspace=@voice-dev/appandnpx tsx test-idempotent-stream.ts.
- Tool snapshots now infer completed/failed states by walking the raw payload (exit codes, tool_result/error flags) when status/result/error are missing, and added regression coverage in
-
Hydrated agent tool calls still lack their responses: after reloading a session we only show the tool metadata pill with a spinner or blank body instead of the diff/read/output sections, so we need to persist and hydrate the parsed payloads (diffs, read text, command stdout) and ensure the hydrated stream entries render them instead of falling back to loading placeholders.
- Parsed edit/read/command payloads are now computed inside
StreamItemreduction, stored alongside tool call metadata, and wired through the stream view + bottom sheet so hydrated sessions immediately render diffs, file contents, and shell output without waiting for new events. Added regression coverage intest-idempotent-stream.tsplus rannpm run typecheck --workspace=@voice-dev/appandnpx tsx test-idempotent-stream.ts.
- Parsed edit/read/command payloads are now computed inside
-
When a tool call finishes we currently render two consecutive pills in the agent stream—the original stays in a perpetual loading state while a brand new “completed” pill shows up underneath. We should be tracking tool call ids and updating the existing entry instead of duplicating it so a single pill transitions cleanly from pending to completed.
- Deduplicated tool call pills by reconciling late-arriving
callIds with the pending entry (matching on provider/server/tool) inpackages/app/src/types/stream.ts, so we now update the existing card instead of appending a new one once completion metadata arrives. Added regression coverage intest-idempotent-stream.tsto mimic a start event that lacks a call id until the completion payload, and rerannpm run typecheck --workspace=@voice-dev/appplusnpx tsx test-idempotent-stream.ts.
- Deduplicated tool call pills by reconciling late-arriving
-
Tool call pills in the agent stream blend into the chat background—add a muted highlight treatment (subtle surface color, border, or backdrop) so they stand out more without overpowering nearby messages.
- Swapped the tool call pill surface to the secondary color, added a drop shadow, and kept status-colored borders so the cards now float above chat bubbles without feeling overpowering. Verified with
npm run typecheck --workspace=@voice-dev/app.
- Swapped the tool call pill surface to the secondary color, added a drop shadow, and kept status-colored borders so the cards now float above chat bubbles without feeling overpowering. Verified with
-
The agent type badge in the agent list still has extra padding compared to the status pill; remove the redundant padding so both badges share the same style and rely on spacing between them rather than padding inside the type badge.
- Removed the internal padding from the provider badge in
packages/app/src/components/agent-list.tsx, so it now mirrors the status pill sizing and we rely on the existing row gap for separation; verified withnpm run typecheck --workspace=@voice-dev/app.
- Removed the internal padding from the provider badge in
-
Follow-up: Codex hydration surfaced tool call responses, but Claude sessions still hydrate with empty tool bodies. Add an automated Claude-specific test that runs a tool call, hydrates the stream, and asserts the diff/read/output content renders; do not mark the hydration tasks complete again until this test passes.
- Added
testClaudeHydratedToolBodiestotest-idempotent-stream.ts, which simulates Claude edit/read/command tool calls, hydrates the stream, and asserts the parsed diff/content/command output persist. Rannpx tsx test-idempotent-stream.tsto cover the new regression.
- Added
-
Despite previous fixes we still see duplicate tool call pills (loading + completed/failed) across Codex and Claude, both live and hydrated streams. Track tool call IDs rigorously, dedupe pending/completed entries, and add regression tests covering real-time and hydrated flows for both providers so this never regresses again.
- Reworked tool call reconciliation so completion events now resolve against the earliest matching pending entry (provider/server/tool + heuristics) instead of the most recent, eliminating duplicate pills even when call IDs arrive late. Added live and hydrated regression tests for both providers in
test-idempotent-stream.ts, and rannpm run typecheck --workspace=@voice-dev/appplusnpx tsx test-idempotent-stream.ts.
- Reworked tool call reconciliation so completion events now resolve against the earliest matching pending entry (provider/server/tool + heuristics) instead of the most recent, eliminating duplicate pills even when call IDs arrive late. Added live and hydrated regression tests for both providers in
-
Duplicate key warnings still appear when opening the agent stream—identify which stream items (thoughts, assistant chunks, tool calls, etc.) are emitting conflicting keys and fix the keying strategy so React no longer logs warnings.
- Hardened the assistant/thought timeline id generator so it now checks for existing ids before committing a suffix, ensuring new entries stay unique even if the stream shrinks, and added regression coverage in
test-idempotent-stream.tsto prove assistant and reasoning ids remain unique after list pruning. Verified vianpx tsx test-idempotent-stream.ts; note Test 1 still intentionally highlights state differences as before. - The warning points at the permission request cards rendered in the stream header; they’re reusing the same key so start the investigation there.
- Hardened the assistant/thought timeline id generator so it now checks for existing ids before committing a suffix, ensuring new entries stay unique even if the stream shrinks, and added regression coverage in
-
Follow-up on the so-called "tests":
test-idempotent-stream.tsonly logs PASS/FAIL (never throws), isn’t hooked to any npm script/CI target, and the Claude fixture doesn’t resemble realtool_resultblocks. Convert these demos into assertions that fail the process, wire them into an npm script/CI check, and update the Claude fixtures to mirror actual SDK payloads so the regression reproduces before anyone claims it’s fixed again.- Swapped the bespoke logger for
node:test+assertso failures now exit non-zero, refreshed every helper to cover actual Claudemcp_tool_use/resultpayloads, and wired the suite intonpm testvia a newtest:streamscript. Rannpm run test:streamto prove the harness passes end-to-end.
- Swapped the bespoke logger for
-
Hydrated sessions are still dropping user messages even though this has allegedly been “fixed” multiple times. Capture the regression in an automated test (hydration snapshot + real-time replay), ensure user messages survive rehydration for both Codex and Claude, and do not check the box until the test fails on current main and passes after the fix.
- Added
testHydratedUserMessagesPersistintest-idempotent-stream.tsto hydrate duplicate-text user prompts across Claude and Codex and verify they remain after live updates, then fixed the regression by giving hydration-only prompts collision-resistant ids viacreateUniqueTimelineId. Rannpx tsx test-idempotent-stream.ts.
- Added
-
Permission request cards rendered in the stream header emit duplicate key warnings because consecutive requests share the same
request.id; derive a composite key so they stay unique per agent even when IDs collide or are missing.- Switched the header map to key permission cards by
${agentId}:${request.id}with a title/name/index fallback so React no longer sees duplicate keys even if providers reuse an id or omit it altogether. Rannpm run typecheck --workspace=@voice-dev/app.
- Switched the header map to key permission cards by
-
Investigate and fix: ERROR Your app just crashed. See the error below. java.lang.NullPointerException: Attempt to read from field 'int android.view.View.mViewFlags' on a null object reference in method 'void android.view.ViewGroup.dispatchDraw(android.graphics.Canvas)' android.view.ViewGroup.dispatchDraw(ViewGroup.java:4396)
- reproducible crash came from Reanimated
entering/exitingtransitions on Android (react-native-reanimated#8422), so we now disable those fade animations for AgentInputArea, GlobalFooter, and AgentStreamView when running on Android to prevent ViewGroup.dispatchDraw from touching a null child; verified withnpm run typecheck --workspace=@voice-dev/app.
- reproducible crash came from Reanimated
-
Follow-up: Replace the ad-hoc stream harness with real vtest coverage—co-locate *.test.ts files next to the code they cover, run them via the existing
npm test(vitest) pipeline instead of bespoke scripts, and ensure the suite exercises both live and hydrated tool-call/message flows before re-checking any of these tasks.- Moved the standalone harness into
packages/app/src/types/stream.test.tsso Vitest now exercises the live/hydrated tool-call + message flows in place, added an app-level Vitest config + script, rewired the rootnpm testto fan out across workspaces, and removed the obsoletetest-idempotent-stream.tsentry point.npm testnow executes both the server suite (currently failing upstream inpackages/server/src/server/agent/providers/claude-agent.test.ts) and the new app tests.
- Moved the standalone harness into