Skip to content

Dev#35

Merged
olegedly merged 13 commits into
mainfrom
dev
Jun 12, 2026
Merged

Dev#35
olegedly merged 13 commits into
mainfrom
dev

Conversation

@olegedly

Copy link
Copy Markdown
Owner

No description provided.

olegedly added 13 commits June 12, 2026 09:37
update main, resilientFetch, toolResultTracker, useChatStore
resilientFetch.test.ts (2 tests):
- AbortError conversion from reader TypeError (regression guard)
- Normal data passthrough (regression guard)

toolResultTracker.test.ts (2 tests):
- Loading true→false tick increment (collapse on stop)
- Loading stays false no tick (false positive guard)

toolResultTracker.ts fix: initialize prevLoading to undefined
to avoid false transition on first effect run when loading=false.
…LM (TDD)

RED: 5 tests for _convert_dict_messages prove that orphaned tool-calls
(produced when the user clicks Stop mid-stream) cause the function
to emit AIMessages with unresolvable tool_calls, which the LLM
provider rejects with a 400 error.

GREEN: add _strip_orphaned_tool_calls() post-processing pass that
removes any tool_call whose tool_call_id lacks a corresponding
ToolMessage in the message list. Also fix content=None handling
in AIMessage construction (AIMessage requires content to be str,
not None).
RED: test proves that calling createNew() when a fresh empty
conversation (title 'New conversation', 0 messages) already exists
creates a second one instead of switching to the existing one.

GREEN: guard in createConversationStore.createNew() scans for an
existing conversation matching title 'New conversation' with
no messages. If found, switches to it instead of creating.
Two existing tests adjusted: they need to set a custom title before
calling createNew() so the guard doesn't intercept them.
…parate blocks (TDD)

RED: two tests prove that when reasoning→tool-call→tool-result→
reasoning arrives, the second reasoning block appends to the first
instead of starting fresh — no ReasoningMessageEndEvent is emitted
at the tool result boundary.

GREEN: add _close_reasoning() call at the top of _observe_tool_result().
When a tool result arrives it closes any open reasoning block, so the
next reasoning chunk from the LLM opens a new block in the correct
chronological position.
resilientFetch: add integration-style 'stops cleanly' test that
exercises the real ReadableStream cancel→pull→signal.aborted path
without mocking getReader. Accepts both {done:true} and AbortError
as valid clean-stop outcomes (differs by runtime).

_convert_dict_messages: add test for the case where ALL tool_calls
are orphaned and the assistant message has None content. After
stripping, the AIMessage should have content='' and no tool_calls.
…arate ThinkingParts (TDD)

RED: 3 tests prove that stepId is missing from STEP_STARTED and
REASONING_MESSAGE_CONTENT events, causing the frontend's StreamProcessor
(which ignores REASONING_MESSAGE_START/END as break no-ops) to
accumulate all reasoning into one ThinkingPart keyed by messageId.

GREEN:
- _ensure_reasoning_open() now emits STEP_STARTED with unique stepId
  before REASONING_MESSAGE_START
- REASONING_MESSAGE_CONTENT carries stepId via extra kwarg
- _close_reasoning() resets _current_reasoning_step_id
- 2 existing tests updated to account for STEP_STARTED at index 0
RED: test proves thinking blocks stay expanded after loading stops.
The issue is that <Index> in SolidJS freezes non-each props, so
nextToolCallTick changes inside <Index> don't propagate to
CollapsibleSection.

The root cause: SolidJS's <Index> and <For> components create
children that capture non-each props at creation time. When
nextToolCallTick changes without the parts array changing, the
children never re-evaluate.

GREEN: introduce StopCollapseContext — a solid Context that
propagates a reactive stop-collapse signal across <Index>/<For>
boundaries. ChatView sets it via a local isLoading→true→false
transition signal. CollapsibleSection consumes it via useContext
and collapses when the signal ticks. This applies to thinking
blocks (Reasoned) and tool result panels alike.
…w' (TDD)

RED: test proves thinking blocks loaded from storage (isLoading=false)
start expanded, and that click '+ New' does not refocus the textarea.

ROOT CAUSE (thinking collapse): ThinkingPartRenderer used
CollapsibleSection default (expanded=true) unconditionally. After
streaming stops, parts persist with isLoading=false but stay expanded
on page reload or conversation switch.

FIX: ThinkingPartRenderer passes expanded={props.isLoading} so
thinking blocks start collapsed when loaded from storage and expanded
only during active streaming. StopCollapseContext handles the
expanded->collapsed transition on stop.

ROOT CAUSE (autofocus): ChatInput only focused on mount and on
isLoading->false transition. '+ New' keeps ChatInput mounted but
triggers neither.

FIX: useChatStore exposes a focusTick signal that increments on
createNew. ChatView threads it to ChatInput, which watches it via
createEffect and re-focuses the textarea on change.
…ection, MessagePartRenderer

update conversations: CollapsibleSection, MessagePartRenderer
…ion.test, toolResultTracker.test, toolResultTracker

update CollapsibleSection, MessagePartRenderer, CollapsibleSection.test, toolResultTracker.test, toolResultTracker
update MessageList, pi committer async commit worker.f96c5101
@olegedly olegedly merged commit b688d37 into main Jun 12, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant