Skip to content

Flora131/feature/add cline support - #22

Merged
flora131 merged 2 commits into
mainfrom
flora131/feature/add-cline-support
Nov 13, 2025
Merged

Flora131/feature/add cline support#22
flora131 merged 2 commits into
mainfrom
flora131/feature/add-cline-support

Conversation

@flora131

Copy link
Copy Markdown
Collaborator

No description provided.

@flora131
flora131 merged commit 7bf50cf into main Nov 13, 2025
@lavaman131
lavaman131 deleted the flora131/feature/add-cline-support branch November 16, 2025 01:58
lavaman131 pushed a commit that referenced this pull request Feb 16, 2026
- Created src/ui/components/parts/reasoning-part-display.tsx
- Component renders ReasoningPart with thinking emoji and duration
- Displays dimmed text using theme colors (colors.muted)
- Shows 'Thinking...' during streaming, 'Thought (X.Xs)' when complete
- Created src/ui/components/parts/index.ts with exports
- Task #22 complete
lavaman131 added a commit that referenced this pull request Feb 17, 2026
…es (#212)

* feat(ui): add getReadyTasks() dependency filter to task-order

- Add getReadyTasks() exported function for filtering pending tasks
- Returns only tasks whose blockedBy dependencies are all completed
- Reuses normalizeTaskId() for consistent ID handling
- Add comprehensive test suite with 15 new test cases
- All tests pass with 100% function coverage and 99.13% line coverage
- Type-safe and deterministic implementation

Supports DAG orchestration by identifying ready-to-execute tasks.
Completes task #1 from workflow.

* feat(ui): add detectDeadlock() with cycle and error dependency diagnostics

- Add DeadlockDiagnostic type with cycle, error_dependency, and none variants
- Implement detectDeadlock() function that:
  - Detects circular dependencies using DFS algorithm
  - Identifies pending tasks blocked by error tasks
  - Reuses normalizeTaskId() for consistent ID handling
  - Returns detailed diagnostic information
- Add comprehensive test suite with 18 focused test cases covering:
  - Cycle detection (simple, complex, self-referential)
  - Error dependency detection
  - Edge cases (empty lists, invalid IDs, unknown blockers)
  - Priority handling (cycles before error dependencies)
- All 40 tests pass with 99.12% line coverage

* feat(ui): replace serial Ralph worker loop with DAG orchestrator

- Replace serial worker loop in fresh run flow with runDAGOrchestrator call
- Replace serial worker loop in resume flow with runDAGOrchestrator call
- Remove unused imports: buildTaskListPreamble, saveWorkflowSession
- Update test to mock SubagentGraphBridge for DAG orchestrator
- Update test expectations to reflect DAG orchestrator behavior (completes all pending tasks)
- Preserve logging/progress UX and persistence semantics from tasks #6-#12

This change enables parallel task execution while maintaining compatibility with existing workflow state management.

* fix(ui): resolve buildContentSegments regression failures

Fix 5 failing adversarial formatting tests in content segment builder:

- Skip task list insertion when tasksExpanded is false to avoid
  splitting text for hidden/collapsed task panels
- Remove trimStart() on remaining text after tool insertions to
  preserve leading whitespace boundaries
- Restrict paragraph splitting to text truly interleaved between
  non-text segments and skip fenced code blocks

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor(ralph): remove auto orchestrator from run/resume paths

Remove automatic runDAGOrchestrator() invocation from both /ralph run
and resume command paths. After bootstrapping session and task state,
control now returns to the main agent for manual worker dispatch.

- Remove runDAGOrchestrator() function and all orchestrator-only imports
- Update resume test to verify normalized state without auto-completion
- Remove DAG orchestrator integration and E2E test suites (dead code)
- Update module description to reflect manual dispatch model

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor(ralph): remove obsolete orchestrator wiring and imports

Remove dead orchestrator infrastructure from workflow-commands.ts that
was left behind after removing auto orchestrator calls in task #1:

- Remove graph-related imports (CompiledGraph, BaseState, NodeDefinition,
  AtomicWorkflowState, setWorkflowResolver, CompiledSubgraph)
- Simplify WorkflowMetadata interface: remove generic type parameter and
  createWorkflow field (graphs are never executed)
- Remove entire workflow registry and resolution section (~150 lines):
  workflowRegistry, initializeRegistry, getWorkflowFromRegistry,
  resolveWorkflowRef, hasWorkflow, getWorkflowNames,
  refreshWorkflowRegistry
- Remove initializeWorkflowResolver and createWorkflowByName functions
- Remove WORKFLOW_DEFINITIONS export alias
- Simplify BUILTIN_WORKFLOW_DEFINITIONS: remove dummy graph node creation
- Update registerWorkflowCommands to not call initializeWorkflowResolver
- Clean up re-exports in commands/index.ts and ui/index.ts

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(ui): align sub-agent/task streaming with ralph bootstrap

Bootstrap Ralph task context after planning/resume so manual worker dispatch starts with task metadata in-session. Improve tool/sub-agent correlation and content insertion ordering so task lists, agent trees, and tool events render in stable chronological order.

Refactor skill and parallel-agent status indicator helpers, pin Ralph task updates to the panel while restoring inline task rendering elsewhere, and add focused regression tests plus related specs/research docs.

Assistant-model: GitHub Copilot CLI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(fmt,review): format + add review step

* test(ui): add comprehensive background agent lifecycle tests

Add parallel-agent-background-lifecycle.test.ts with 19 tests covering:

Unit Tests (8):
- Agent creation with mode=background/async/sync
- tool.complete skips finalization for background agents
- tool.complete transitions sync agents to completed
- subagent.complete transitions background agents to completed/error
- interrupt sets background agent to interrupted

Integration Tests (11):
- Full background lifecycle: spawn → tool.complete → subagent.complete
- Mixed sync+background agents finalize correctly
- Stream finalization hasActive checks include background agents
- Stream finalization map skips background agents
- Field preservation during transformations
- Edge cases (empty arrays, ID matching, etc.)

All tests pass (19/19). Total test suite: 1084 tests passing.

Context: Tests verify the lifecycle state management changes that prevent
background-mode Task agents from being prematurely marked as completed.

* fix(ui): prevent premature completion of background sub-agents

Extract mode parameter at agent creation time to set status: "background"
and background: true flag for background/async Task agents. Guard all five
finalization sites to skip agents with the background flag, allowing
subagent.complete to be the sole terminal event.

- Agent creation: set background status and flag when mode=background|async
- tool.complete: skip status/currentTool/durationMs update for bg agents
- Cleanup helper: include "background" in active agent check
- Stream finalization (3 paths): include "background" in hasActive check
- Add 19 unit/integration tests for background lifecycle transitions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(ui): render all components inline except Ralph task list panel

- Move compaction summary from outside scrollbox to inside scrollbox
- Remove 'background' from hasActive checks so background agents
  don't block stream completion
- Fix subagent.complete handler to allow background agent updates
- Add backgroundAgentMessageIdRef to track post-stream completion
  updates for background agents in baked messages
- Keep background agents in live state after stream finalization
  so completion events can propagate to the correct message
- Improve task segment rendering with border and progress text
- Fix setMessagesWindowed purity (defer side-effects to useEffect)
- Fix TS errors in background lifecycle tests (Object possibly undefined)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(parts): add ToolState discriminated union type

- Add ToolState type with 5 states: pending, running, completed, error, interrupted
- Enforce state machine: pending → running → (completed|error|interrupted)
- Export ToolState from parts module
- Satisfies spec §5.3 Tool State Machine requirements

* feat(ui): implement useThrottledValue hook for 100ms text debounce

- Create useThrottledValue hook with generic type parameter
- Throttle interval defaults to 100ms
- Uses refs for last update time tracking
- Cleans up pending timeouts on unmount
- Add hook export to hooks index
- Add basic validation tests

Implements task #20 from parts-based rendering spec §5.3

* feat(parts): define all Part type interfaces and Part discriminated union

- Add imports for HitlResponseRecord, PermissionOption, ParallelAgent, TaskItem, MessageSkillLoad, McpSnapshotView, and ContextDisplayInfo
- Define concrete Part type interfaces:
  * TextPart: accumulated text with streaming state
  * ReasoningPart: reasoning content with duration
  * ToolPart: tool execution with state machine and HITL support
  * AgentPart: parallel agent tracking
  * TaskListPart: task list with expansion state
  * SkillLoadPart: skill loading status array
  * McpSnapshotPart: MCP server snapshot view
  * ContextInfoPart: context display information
  * CompactionPart: message compaction summary
- Define Part discriminated union type for all part types
- Export all new types from parts module index

Tasks #3 and #4 complete.

* feat(parts): add optional parts field to ChatMessage interface

- Add Part type import from parts module
- Add optional parts?: Part[] field to ChatMessage interface
- Field placed after streaming field as per spec
- Maintains backward compatibility with optional operator
- Documentation comment added for chronological ordering

Task #6 complete. Unblocks tasks #7, #9, and #16.

* feat(ui): create ReasoningPartDisplay renderer component

- Created src/ui/components/parts/reasoning-part-display.tsx
- Component renders ReasoningPart with thinking emoji and duration
- Displays dimmed text using theme colors (colors.muted)
- Shows 'Thinking...' during streaming, 'Thought (X.Xs)' when complete
- Created src/ui/components/parts/index.ts with exports
- Task #22 complete

* feat(parts): add optional parts field to ChatMessage interface

- Add Part type import from parts module
- Add optional parts?: Part[] field to ChatMessage interface
- Field placed after streaming field as per spec
- Maintains backward compatibility with optional operator
- Documentation comment added for chronological ordering

Task #6 complete. Unblocks tasks #7, #9, and #16.

* test(parts): add unit tests for shouldFinalizeOnToolComplete guard

- Create comprehensive test suite for shouldFinalizeOnToolComplete()
- Test all agent status types (pending, running, completed, error, interrupted, background)
- Test background flag behavior (agent.background = true)
- Test background status behavior (status = 'background')
- All 8 tests pass with 100% code coverage

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(parts): create ToolPartDisplay with inline HITL overlay support

- Created src/ui/components/parts/tool-part-display.tsx
  * Renders ToolPart with tool execution status via ToolResult component
  * Displays active HITL questions inline using UserQuestionInline
  * Shows completed HITL responses as compact records using CompletedHitlDisplay
  * Implements toolStateToStatus() converter from ToolState to ToolExecutionStatus
  * Follows parts-based rendering architecture (spec §5.5)

- Updated src/ui/components/parts/index.ts
  * Added ToolPartDisplay and ToolPartDisplayProps exports

Key architectural changes:
- HITL questions render inline after tool output (not as fixed overlays)
- Uses discriminated union ToolState for tool execution states
- Bridges to existing ToolResult component for consistent tool output rendering
- Supports both pendingQuestion (active) and hitlResponse (completed) states

Implements Task #24 from parts-based rendering specification

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(tests): add TypeScript type assertions for array access safety

- Add non-null assertions for array accesses in id.test.ts and store.test.ts
- Cast Part[] elements to TextPart when accessing content property
- Fixes strict TypeScript checks while maintaining test correctness
- All tests still pass with 100% coverage

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(parts): dual-populate AgentPart on sub-agent start events

Modify the sub-agent update effect in chat.tsx to create/update
AgentPart in message.parts[] alongside the existing parallelAgents
field. This enables parts-based rendering of sub-agents while
maintaining backward compatibility with legacy rendering.

Implementation:
- Import createPartId, upsertPart, and AgentPart type
- Find or create AgentPart in parts[] array during both:
  * Active streaming message updates
  * Background agent completion updates
- Use upsertPart() for sorted insertion/update
- Preserve all existing behavior (dual population pattern)

Testing:
- All 469 existing UI tests pass
- Type checking passes without errors
- No behavior changes to legacy rendering path

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(parts): implement handleTextDelta() for text streaming with tool splits

Implements handleTextDelta() function that handles text streaming with natural
tool boundary splitting. The function:
- Appends to existing streaming TextPart if isStreaming is true
- Creates new TextPart if previous is finalized or doesn't exist
- Naturally handles tool-boundary text splitting

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* test(parts): add unit tests for handleTextDelta and getMessageText

- Add handlers.test.ts with 4 test cases for handleTextDelta
  - Creates new TextPart on empty parts array
  - Appends to existing streaming TextPart
  - Creates new TextPart when last is not streaming
  - Handles undefined parts initialization
- Add helpers.test.ts with 4 test cases for getMessageText
  - Returns empty string for undefined/empty parts
  - Concatenates multiple TextPart contents
  - Ignores non-text parts
- All 8 tests pass with 100% function and line coverage

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(parts): create PART_REGISTRY mapping part types to renderers

- Create src/ui/components/parts/registry.tsx with PART_REGISTRY
- Map all Part types to their corresponding renderer components
- Export PartRenderer type and PART_REGISTRY from index.ts
- Registry enables dynamic dispatch based on Part discriminant

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(parts): dual-populate ToolPart on tool.start events

- Finalize streaming TextPart (set isStreaming: false) when tool starts
- Create new ToolPart with status: running and startedAt timestamp
- ToolPart includes toolCallId, toolName, input from SDK event
- Maintains existing tool start behavior (toolCalls array, offsets, etc.)
- Uses upsertPart() for chronological insertion into parts[] array

Implements Task #14 per spec §5.4 dual-population requirements

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(parts): dual-populate TextPart on text streaming chunks

Modify all three text chunk handlers in chat.tsx to create/update TextPart
alongside the existing legacy content field:

1. onChunk callback (line 2452) - workflow initialization streaming
2. handleChunk (line 3355) - main stream message handler
3. handleChunk (line 4818) - queued message handler

Implementation:
- Import handleTextDelta from parts/handlers.ts
- Call handleTextDelta(msg, chunk) before updating message
- Spread parts array into message update: { ...msg, parts: withParts.parts }
- Existing content accumulation unchanged: content: msg.content + chunk

This implements dual population - the existing code continues to work
exactly as before, but we ALSO populate the parts[] array with TextPart
for the new parts-based rendering system.

Backward Compatible:
- parts field is optional on ChatMessage
- No changes to existing content field behavior
- All 477 existing tests pass

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(parts): apply shouldFinalizeOnToolComplete guard to prevent premature stream completion

Modify the stream finalization logic in index.ts to use the
shouldFinalizeOnToolComplete() guard when checking for active
agents. This prevents the stream from being marked as complete
prematurely when background agents are still running.

The guard returns false for background agents (either via the
background flag or status), ensuring that:
- Background agents can continue running after tool.complete
- Stream remains active until background agents reach terminal state
- subagent.complete events are properly processed

The dual population of AgentPart was already implemented in task #16
via the parallelAgents effect in chat.tsx, so this task focuses on
applying the finalization guard to prevent the critical bug where
background agents cause premature stream completion.

Implementation:
- Import shouldFinalizeOnToolComplete from parts/index.ts
- Update hasActiveAgents check in stream finalization (line 1188)
- Keep stream active if any agent returns false from guard

Testing:
- All 19 background agent lifecycle tests pass
- All 8 shouldFinalizeOnToolComplete guard tests pass
- TypeScript compilation successful

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(parts): create MessageBubbleParts component rendering from parts[]

Implement MessageBubbleParts component that renders ChatMessage using
the parts-based rendering system instead of buildContentSegments().

- Create src/ui/components/parts/message-bubble-parts.tsx
- Export component from parts index.ts
- Component dispatches each part to its renderer via PART_REGISTRY
- Returns null if message has no parts
- Passes isLast flag to indicate final part in sequence

Implements task #29 per spec §5.5.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(parts): add usePartsRendering feature flag toggle

During Phase 3 migration, this defaults to false (legacy rendering).
Toggle via ATOMIC_PARTS_RENDERING environment variable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(parts): add shouldFinalizeOnToolComplete guard to tool.complete handler

Apply the shouldFinalizeOnToolComplete() guard function to all three
finalization paths in the tool.complete handler (index.ts lines 655-732)
to prevent premature stream completion when a background agent's tool
completes.

Also includes Task #18 implementation: Modify handlePermissionRequest to
set pendingQuestion on ToolPart for inline HITL rendering.

Changes (Task #33):
- Replace inline a.background checks with shouldFinalizeOnToolComplete(a)
  guard in the ID-based correlation path (lines 664-676)
- Add shouldFinalizeOnToolComplete(a) check to the fallback path that
  finds the last running agent without a result (line 692)
- Add shouldFinalizeOnToolComplete(a) check to the no-result completion
  path for eager agents (line 725)

Changes (Task #18):
- Update handlePermissionRequest to accept optional toolCallId parameter
- Find matching ToolPart by toolCallId in message.parts[] array
- Set pendingQuestion field on ToolPart with HITL request data
- Preserve existing overlay dialog behavior during dual-population

The guard returns false for background agents (via background flag or
status), ensuring:
- Background agents continue running after tool.complete
- Stream remains active until background agents reach terminal state
- subagent.complete events are properly processed
- Only sync/foreground agents transition to completed on tool.complete

Testing:
- All 19 background agent lifecycle tests pass
- All 55 parts unit tests pass with 100% coverage
- TypeScript compilation successful (no new errors)

Spec reference:
§5.4 Fix 3: Stream Deferral and Finalization Guards

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(parts): complete Task #18 - clear pendingQuestion and set hitlResponse on ToolPart

Complete the permission request handling by updating handleQuestionAnswer
to clear pendingQuestion and set hitlResponse on the matching ToolPart
when the user responds to a HITL question.

Changes:
- Add toolCallId field to PermissionRequestedEventData interface (types.ts)
- Update handleQuestionAnswer to find matching ToolPart by toolCallId
- Clear pendingQuestion field when user responds
- Set hitlResponse field with user's answer
- Maintain dual-population with legacy toolCalls array
- Add comprehensive unit tests for permission request handling

This completes Task #18 implementation started in commit 0eb3136,
which added pendingQuestion setting in handlePermissionRequest.

Testing:
- All 5 permission request tests pass
- Existing HITL tests continue to pass
- TypeScript compilation successful

Spec reference: §5.4 SDK Event → Part Updates (permission.requested)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(ui): add shouldFinalizeOnToolComplete guard to stream finalization effect

Prevent premature stream finalization when background agents are still running.
The guard checks all parallel agents before allowing finalization to proceed,
ensuring background agents complete before the stream is finalized.

This addresses one of the 4+ finalization paths identified in the spec,
complementing the existing guards in index.ts and the tool.complete handler.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* test(parts): add integration tests for dual-population output comparison

Verify the dual-population mechanism produces consistent parts[] data
alongside the legacy content/segments model during the transition period.

Test coverage:
- Text streaming produces TextPart with matching content
- Tool start creates ToolPart and finalizes TextPart
- Tool complete updates ToolPart state transitions
- Tool error updates ToolPart state to error
- Sub-agent creates AgentPart in parts[]
- Permission request sets pendingQuestion on ToolPart
- HITL response clears pendingQuestion and sets hitlResponse
- Multiple text-tool-text sequences create separate parts in order
- AgentPart updates preserve existing parts

Also fix TypeScript strict mode issues:
- Add undefined checks in store.ts for array access operations
- Fix TextPartDisplay to use OpenTUI's fg style prop instead of color
- Remove unused isLast parameter from TextPartDisplay

All 64 parts tests pass with 100% code coverage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(parts): wire feature flag into chat.tsx to switch old/new rendering

- Import usePartsRendering hook and MessageBubbleParts component
- Call usePartsRendering() in MessageBubble component
- Add conditional rendering for assistant messages with parts[]
- Falls back to legacy buildContentSegments() when flag is disabled
- All existing rendering code preserved intact

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* test(parts): add integration tests for HITL inline rendering

Write comprehensive integration tests verifying that HITL (Human-in-the-Loop)
permission requests are correctly represented inline within the parts model,
replacing the old fixed-position overlay approach.

Tests cover:
- Permission request sets pendingQuestion on ToolPart
- HITL response clears pendingQuestion and sets hitlResponse
- Multiple HITL requests on different tools maintain independence
- ToolPart without HITL has no pendingQuestion
- HITL response preserves tool state
- pendingQuestion has all required fields (requestId, header, question, options, multiSelect, respond)
- Multi-select HITL questions with multiple options
- Cancelled/declined HITL responses
- Custom input response mode
- Chat about this response mode

All 10 tests pass. Tests use bun:test framework and follow existing patterns
from the parts model test suite.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* test(parts): add integration tests for HITL inline rendering

Write comprehensive integration tests verifying that HITL (Human-in-the-Loop)
permission requests are correctly represented inline within the parts model,
replacing the old fixed-position overlay approach.

Tests cover:
- Permission request sets pendingQuestion on ToolPart
- HITL response clears pendingQuestion and sets hitlResponse
- Multiple HITL requests on different tools maintain independence
- ToolPart without HITL has no pendingQuestion
- HITL response preserves tool state
- pendingQuestion has all required fields (requestId, header, question, options, multiSelect, respond)
- Multi-select HITL questions with multiple options
- Cancelled/declined HITL responses
- Custom input response mode
- Chat about this response mode

All 10 tests pass. Tests use bun:test framework and follow existing patterns
from the parts model test suite.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* test(parts): write background agent lifecycle tests

Verify that background agents are not prematurely finalized across all
finalization paths. Tests cover shouldFinalizeOnToolComplete() guard
behavior for:
- Background vs foreground agents
- Different agent statuses (running, completed, pending, error, interrupted)
- Mixed agent scenarios
- Edge cases (undefined background flag, both flag and status set)

All 15 tests pass with 100% coverage of guards.ts.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* test(parts): write E2E test for complete message stream render order

Implement comprehensive E2E test suite for verifying complete order of parts
in a message after a full streaming session with text, tools, agents, and HITL
events.

Implementation:
- Created src/ui/parts/stream-order.test.ts with 12 comprehensive test cases
- Tests verify both part types AND chronological ordering via monotonically
  increasing IDs
- Simulates real streaming scenarios with actual handler functions

Test Scenarios:
1. Simple text-only stream: Text deltas → verify single TextPart
2. Text → Tool → Text sequence: Verify [TextPart, ToolPart, TextPart]
3. Text → Tool → HITL → Response → Text: Verify full HITL flow maintains order
4. Text → Multiple tools → Text: Verify [TextPart, ToolPart, ToolPart, TextPart]
5. Agent spawn mid-stream: Text → Agent spawn → Tool in agent
6. Complex realistic scenario: Text → Reasoning → Tool1 (with HITL) → Tool2 → Agent → Text
7. Parts maintain chronological order via IDs: Verify each part.id is lexicographically greater
8. Empty stream produces no parts: Edge case for no streaming events
9. Consecutive reasoning parts maintain order: Multiple reasoning parts in sequence
10. Interleaved text and tool calls: Complex interleaving pattern
11. Background agent does not break ordering: Background agent survives
12. HITL updates preserve tool order: Updates don't change IDs

Key Features:
- Uses bun:test framework
- Tests data flow, not rendering (no React components)
- Helper functions for creating mock messages, parts, agents, and HITL
- verifyMonotonicIds() helper ensures chronological ordering
- 100% code coverage for handlers.ts, id.ts, store.ts
- All 12 tests pass, 133 expect() calls

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* test(parts): add E2E tests for HITL inline position and sticky scroll

Verify that HITL permission requests appear inline at correct positions
within the parts model, not as fixed overlays. Tests cover:

- HITL appearing at correct ToolPart position after text → tool → request
- HITL position is inline with tool (not separate part)
- hitlResponse replacing pendingQuestion at same position
- Multiple sequential HITL requests maintaining correct positions
- HITL on second tool in sequence with first tool completed
- HITL position persisting across message updates and streaming
- Complex scenarios with mixed HITL states across multiple tools
- Order preservation when responding to HITL questions

All 9 tests pass with 100% coverage on handlers, id, and store modules.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* test(parts): write E2E test for message eviction with parts model

Tests verify that the parts model works correctly with message window
eviction (MAX_VISIBLE_MESSAGES = 50 with messageWindowEpoch remount).

Test cases:
- Parts survive message object identity change (shallow copy)
- Parts are serializable (JSON.stringify/parse)
- Large parts array (100+) handles eviction
- Parts maintain order after message copy
- Empty parts array after eviction (graceful handling)
- Parts array is not shared reference across messages

All tests pass with 100% coverage of id.ts functions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor(parts): deprecate buildContentSegments() and ContentSegment type

These legacy rendering functions are replaced by parts-based rendering
via MessageBubbleParts. They will be fully removed after the
usePartsRendering feature flag is removed (Phase 5 cleanup).

Changes:
- Add @deprecated annotation to ContentSegment interface
- Add @deprecated annotation to buildContentSegments() function
- Document replacement: use MessageBubbleParts instead
- Note: will be removed when feature flag is removed

Testing:
- bun test src/ui/parts/ (136 tests pass, 100% coverage)
- bun run typecheck (no errors in chat.tsx)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor(parts): deprecate legacy offset fields in ChatMessage

Add @deprecated annotations to contentOffsetAtStart, agentsContentOffset,
and tasksContentOffset fields. These legacy offset tracking fields will be
removed when the parts-based rendering feature flag is removed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor(parts): deprecate legacy content:string field in ChatMessage

The parts-based model replaces the monolithic content string with structured parts[] array. Mark content field as deprecated while maintaining it for the legacy rendering path and dual-population.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor(parts): deprecate usePartsRendering feature flag

Mark usePartsRendering as temporary migration flag to be removed
once parts-based rendering is fully validated in production.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore(parts): remove unused isLast parameter from ToolPartDisplay

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: exclude vendored docs from typecheck and test discovery

The docs/ directory contains vendored reference code (opencode, opentui)
with unresolved dependencies that fail tsc and bun test. Exclude docs/
from tsconfig and scope test discovery to src/ so pre-commit hooks pass
without requiring vendored dependencies to be installed.

Assistant-model: Claude Code

* fix(ui): filter sub-agent tool calls from main chat display

Sub-agent tool calls (attributed to running parallel agents) were being
dispatched through both the parallel agents tree and the main chat
tool-call handlers, causing duplicate display. Track sub-agent tool IDs
and gate toolStartHandler/toolCompleteHandler so only non-subagent tools
appear in the message parts and ctrl+o transcript.

Assistant-model: Claude Code

* refactor(ui): complete parts-based rendering migration with design system

Replace legacy offset-based buildContentSegments() with parts-driven
getRenderableAssistantParts() that synthesizes tool, agent, task-list,
MCP snapshot, and context-info parts directly from message data.

Key changes:
- Remove buildContentSegments(), ContentSegment, and content offset
  tracking (agentsContentOffset, tasksContentOffset, contentOffsetAtStart)
- Remove usePartsRendering feature flag and hook
- Add SPACING constants and TASK icon set for consistent layout tokens
- Overhaul task list indicator with numbered rows, left rail, progress
  bar, and status labels
- Simplify parallel agents tree (static indicators, remove blink)
- Improve HITL tool rendering with dedicated display path
- Add circle indicator prefix to assistant text parts
- Remove file-content loading from @mention processing (metadata only)
- Delete obsolete tests for buildContentSegments and skill-indicator e2e

Assistant-model: Claude Code

* fix(ui): use run_in_background for background agent detection with isAsync fallback

Switch background agent detection from checking mode="background"|"async"
to checking input.run_in_background === true, aligning with the actual
Task tool API. Add isAsync fallback in parseTaskToolResult to retroactively
mark agents as background when the tool result indicates async execution.

Assistant-model: Claude Code

* refactor(ui): extract TaskListBox as shared presentational component

Split TaskListPanel into a reusable TaskListBox (bordered container with
progress header, bar, and task rows) and a file-driven TaskListPanel
wrapper. TaskListPartDisplay now uses TaskListBox directly. Remove unused
sessionId prop from TaskListPanel.

Assistant-model: Claude Code

* feat(ui): add skill load indicator for builtin skills

Track skill loads in chat messages with session-level deduplication via
loadedSkillsRef. Render SkillLoadPart in assistant message parts for
selected builtin skills (prompt-engineer, frontend-design,
testing-anti-patterns). Also remove now-unused sessionId prop from
TaskListPanel usage.

Assistant-model: Claude Code

* refactor(ui): simplify completed HITL response display

Replace bordered badge style in CompletedHitlDisplay with a compact
single-line format matching ToolResult headers: status icon + label +
question + indented response. Simplify HITL display text for declined
and chat_about_this response modes.

Assistant-model: Claude Code

* feat(sdk): add Skill and MultiEdit to allowed tool names

Assistant-model: Claude Code

* fix(ui): add skill-loaded directive to prevent model re-invocation of expanded skills

Prepend a <skill-loaded> tag when sending expanded builtin skill
prompts so the model acts on the already-expanded content rather than
re-loading the raw skill via the Skill tool. Also clarify in the
capabilities system prompt that listed skills are user-invocable and
the model should use the Skill tool directly.

Assistant-model: Claude Code

* refactor(ui): consolidate part spacing via parent gap instead of per-child margins

Move inter-part spacing responsibility to the parent MessageBubbleParts
container using gap={SPACING.ELEMENT}. Remove marginBottom from child
part components (AgentPartDisplay, CompactionPartDisplay, ToolPartDisplay,
ToolResult) to avoid double-spacing.

Assistant-model: Claude Code

* refactor(ui): simplify task list display and remove maxWidth constraint

Remove zero-padded index numbers from task items and the RUNNING status
label (keep FAILED). Drop the maxWidth prop from TaskListBox and
simplify width calculations. Use conditional scrollbox only when items
exceed the scroll threshold instead of always wrapping in scrollbox.

Assistant-model: Claude Code

* chore(deps): bump sdk and dev dependency versions

Update @anthropic-ai/claude-agent-sdk to ^0.2.44, @github/copilot-sdk
to ^0.1.24, @opencode-ai/sdk to ^1.2.6, @clack/prompts to ^1.0.1,
oxlint to ^1.48.0, and type packages.

Assistant-model: Claude Code

* feat(skills): materialize builtin skills as SKILL.md files for SDK discovery

Write BUILTIN_SKILLS to .claude/skills/<name>/SKILL.md at startup so
each SDK's native skill discovery mechanism (Claude Skill tool, Copilot
skillDirectories, OpenCode server) can find them. Files are only
rewritten when content changes. The generated directory is gitignored.

Assistant-model: Claude Code

* refactor(sdk): add configurable thinking/reasoning effort to Claude client

Add maxThinkingTokens to SessionConfig and ReasoningEffort type with
getReasoningEffort() helper. Thinking mode is now adaptive for opus and
budget-based (defaulting to 16000 tokens) for other models. Also
reformats claude-client.ts to consistent 4-space indentation and line
wrapping.

Assistant-model: Claude Code

* refactor(ui): replace ralph resume with task loop, add markdown rendering to parts, and fix streaming state

Replace the ralph --resume command with an autonomous task loop that
continues dispatching workers until all tasks complete. Thread syntaxStyle
through the parts rendering pipeline so text parts render as <markdown>
and reasoning parts use a dimmed <code filetype="markdown"> variant.
Fix streaming state cleanup (hasRunningToolRef, streamingMeta) on
interrupts, errors, and stream end to prevent spinner hangs. Improve
task list panel with session ID display and blocker sub-lines. Update
research-codebase and create-spec skill prompts with better instructions.

Assistant-model: Claude Code

* chore(agents): add project memory to Claude agents and update worker skill refs

Enable `memory: project` on all Claude agent configs for persistent context.
Remove hardcoded model from OpenCode agents. Update worker.md to reference
the `Skill` tool instead of the removed `SlashCommand` tool.

Assistant-model: Claude Code

* refactor(skills): migrate commands to skills directories with cross-SDK sync

Replace legacy `.claude/commands/` and `.opencode/command/` directories with
unified `.claude/skills/`, `.opencode/skills/`, and `.github/skills/` SKILL.md
files. Add cross-sync materialization so all three SDK directories contain the
full skill catalog. Update init.ts to use `skillsSubfolder` and remove the
per-agent `getCommandsSubfolder` helper. Remove legacy `SKILL_DEFINITIONS`
from skill-commands.ts.

Assistant-model: Claude Code

* fix(ui): improve HITL display, user question styling, and task list rendering

Redesign tool-part-display to show completed HITL responses in a tree
hierarchy with question and answer. Enhance user-question-inline with
header badges, numbered options, and navigation hints. Trim trailing
newlines in text-part-display. Inline blocker info in task-list-indicator
instead of using a separate sub-line. Remove unused sessionId prop from
TaskListPanel. Enable viewportCulling in transcript-view for performance.

Assistant-model: Claude Code

* fix(ui): prevent sub-agent TodoWrite from overwriting ralph task state

Add ralphTaskIdsRef to track known task IDs from the planning phase.
Guard TodoWrite persistence so only updates matching these IDs are
written to tasks.json, preventing sub-agent independent todo lists from
clobbering ralph's persistent task state. Add mergeBlockedBy utility to
preserve task dependency info when agents omit blockedBy in updates.

Assistant-model: Claude Code

* perf(ui): migrate history buffer to NDJSON with append-only writes

Replace JSON array storage with NDJSON (newline-delimited JSON) for the
conversation history buffer. Uses appendFileSync for O(1) writes instead
of read-modify-write. Add in-memory dedup Set to avoid re-reading the
file on each append. Support legacy JSON array migration detection on
read. Batch eviction flushes in chat.tsx. Add extensive test coverage
for windowing lifecycle scenarios (/clear, /compact, Ctrl+O, scale).

Assistant-model: Claude Code

* refactor(ui): clean up command exports and standardize formatting

Remove unused exports from index.ts (initializeCommands, legacy skill
re-exports). Drop 'custom' command category from registry. Remove
backward-compatibility re-export of parseMarkdownFrontmatter from
agent-commands.ts. Standardize indentation in builtin-commands.ts.
Add setRalphTaskIds to test mock context.

Assistant-model: Claude Code

* docs: add research and specs for message truncation and legacy code removal

Add codebase research docs and technical design specs for two planned
efforts: message truncation with dual-view system, and legacy code
removal for skills migration cleanup.

Assistant-model: Claude Code

* chore: remove stale gitignore entries for deleted docs directories

Assistant-model: Claude Code

* feat(ui): auto-collapse older messages to single-line summaries

Replace the manual conversation-collapsed toggle with automatic
collapsing based on recency. Only the last 4 messages (EXPANDED_MESSAGE_COUNT)
render fully; older messages show as collapsed single-line summaries.
Live messages (streaming or with active background agents) are never
collapsed regardless of position.

Add shouldCollapseMessage utility with comprehensive tests.

Assistant-model: Claude Code

---------

Co-authored-by: Developer <dev@example.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
lavaman131 pushed a commit that referenced this pull request Feb 23, 2026
…tation (task #22)

- Add 'test:contracts' script to package.json for running contract parity tests
- Document CI enforcement in background-agent-contracts.ts JSDoc
- Contract tests automatically run in CI via 'bun test' command
- Lefthook pre-commit hook runs 'bun test --bail' which includes contract tests
- All 116 contract parity tests passing (provider, runtime, acceptance, etc.)
lavaman131 pushed a commit that referenced this pull request Feb 23, 2026
…tation (task #22)

- Add 'test:contracts' script to package.json for running contract parity tests
- Document CI enforcement in background-agent-contracts.ts JSDoc
- Contract tests automatically run in CI via 'bun test' command
- Lefthook pre-commit hook runs 'bun test --bail' which includes contract tests
- All 116 contract parity tests passing (provider, runtime, acceptance, etc.)
lavaman131 added a commit that referenced this pull request Feb 23, 2026
…265)

* refactor(ui): extract stream pipeline and add background agent management

Extract streaming event handling from the monolithic chat component into
dedicated, testable modules:

- parts/stream-pipeline.ts: unified event reducer for text, thinking,
  tool, HITL, and agent streaming events
- utils/loading-state.ts: completion summary and loading indicator logic
- utils/background-agent-footer.ts: active background agent resolution
- utils/background-agent-termination.ts: Ctrl+F double-press termination
- utils/background-agent-tree-hints.ts: parallel agents header hints
- components/background-agent-footer.tsx: footer status component

Additional fixes:
- Normalize Windows line endings (CRLF) in markdown text handling
- Apply text normalization to task tool result parsing
- Expand guards with hasActiveForegroundAgents and shouldFinalizeDeferredStream

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* docs(research): add branch task breakdown for TUI streaming rendering

Document the grouped issues (#259, #258, #254, #248, #231) being
addressed on the fix/tui-streaming-rendering branch with rationale
for their grouping under the streaming content rendering pipeline.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* docs: track cross-agent E2E dependency blockers

List environment provisioning issues causing test failures for
protocol ordering, claude rendering, unified event parity, copilot
client, and opencode events test suites.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(ui): resolve streaming render inconsistencies

Harden tool completion timing and preserve HITL responses when syncing tool
parts.

Improve streaming output rendering by removing text-part status prefixes,
normalizing reasoning duration labels, and converting markdown task checkboxes
to unicode symbols for reliable TUI display.

Add focused tests covering duration formatting, invalid startedAt handling, and
markdown checkbox normalization.

Assistant-model: GitHub Copilot CLI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* test(ui): add thinking stream interleaving and handoff integration coverage

Assistant-model: openai/gpt-5.3-codex

* chore: remove resolved issues tracker and debug screenshot

These files were used during development and are no longer needed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(sdk,ui): add thinking source identity tracking to streaming pipeline

Propagate provider-native thinking source keys (block index, reasoning
ID, part ID) through all three SDK clients (Claude, Copilot, OpenCode)
and into the UI streaming pipeline.

- Add thinkingSourceKey to MessageDeltaEventData and stream metadata
- Track thinking source lifecycle (create/update/finalize/drop) with
  diagnostics support
- Validate thinking-meta events against message ID and stream generation
  to prevent stale/cross-source bleed
- Build stable React render keys from reasoning source identity
- Filter pending ask-user questions from message bubble rendering
- Add comprehensive tests for source identity, interleaving, and
  validation across all SDK clients

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* docs: add thinking tag stream grouping research and spec

Add research documents for thinking source identity tracking and
background agents UI, plus the implementation spec for thinking tag
stream grouping.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor: align getBackgroundTerminationDecision with canonical discriminated union type

- Remove old BackgroundTerminationDecision interface from background-agent-termination.ts
- Import and re-export BackgroundTerminationDecision from background-agent-contracts.ts
- Update getBackgroundTerminationDecision to return discriminated union:
  - { action: 'none' } when no active background agents
  - { action: 'warn', message: '...' } on first press
  - { action: 'terminate', message: '...' } on second press
- Update chat.tsx to use new discriminated union pattern
- Update all tests to match new return type
- All tests passing, no type errors

* refactor: align footer resolver and component with canonical contract

- Import and use BACKGROUND_FOOTER_CONTRACT in footer files
- Replace hardcoded 'ctrl+f terminate' with contract value
- Add contract validation tests
- Add test for footer visibility threshold

Tasks #7 + #8 complete. All footer UX now driven by the canonical
contract, eliminating hardcoded behavior.

* feat(telemetry): add background termination tracking and metrics

- Add TuiBackgroundTerminationEvent interface with action, activeAgentCount, interruptedCount
- Add trackBackgroundTermination method to TuiTelemetrySessionTracker
- Track noop/warn/execute counters for session summary
- Include counters in TuiSessionEndEvent and TuiSessionSummary
- Supports observability for Ctrl+F keyboard termination flow

Related to tasks #11 and #12 in workflow

* feat(ui): add structured debug logs for background termination state transitions

- Add console.debug call after decision computation with pressCount and activeAgents
- Add debug log in none/noop branch
- Add debug log in terminate branch with interruptedIds and remainingCount
- Add debug log in warn/armed branch
- All logs prefixed with [background-termination] for filtering
- Uses console.debug for structured logging

Related to task #11 in workflow

* test(ui): add parent callback integration tests for background agent termination

* test(ui): add Ctrl+O non-conflict integration test for background termination

- Create background-agent-keybinding-nonconflict.test.ts
- Verify Ctrl+O (transcript toggle) does NOT conflict with Ctrl+F (termination)
- Verify Ctrl+C (interruption) does NOT conflict with Ctrl+F (termination)
- Test modifier exclusion (Ctrl+Shift+F, Ctrl+Meta+F not detected)
- Comprehensive test of all common Ctrl+key combos (a-z)
- All 8 tests pass with 32 expect() calls

* test(ui): add E2E provider parity matrix tests for background agent contracts

* test(ui): add E2E runtime parity tests for background agent contracts

Add comprehensive test suite verifying background agent contract functions
produce deterministic, consistent results invariant across runtime paths
(dev via 'bun run' vs compiled production binary).

Per spec (specs/background-agents-ui-issue-258-parity-hardening.md), dev
and production runtime paths share startChatUI entry point. Contract
functions are pure JavaScript with no runtime-conditional branching.

Test coverage:
- Contract constants frozen/deterministic (BACKGROUND_FOOTER_CONTRACT,
  BACKGROUND_TREE_HINT_CONTRACT)
- Pure function determinism (getBackgroundTerminationDecision,
  interruptActiveBackgroundAgents, getActiveBackgroundAgents,
  buildParallelAgentsHeaderHint, formatBackgroundAgentFooterStatus)
- Idempotency (multiple calls with same args yield same result)
- No environment-conditional branching (no process.env/import.meta checks)
- Module import stability (all exports accessible with expected types)
- Function signature stability (parameter counts remain consistent)

This is a 'canary' test documenting and enforcing invariance rather
than testing complex logic.

Issue #258 Task #20

* test(ui): add acceptance tests for issue #258 background agent UX contracts

- Create fixture-based acceptance tests at background-agent-acceptance.test.ts
- Validate exact footer text/behavior: 'ctrl+f terminate' hint, agent count visibility
- Validate Ctrl+F double-press flow: warn → terminate → agent termination confirmation
- Validate tree hints: running/complete/default states with exact wording
- Test cross-surface consistency: ctrl+f/ctrl+o references, 'terminate' keyword
- Test UX polish: separator style (·), lowercase keybindings, pluralization
- All 21 acceptance tests pass, providing machine-readable screenshot equivalents
- Tests serve as canonical specification for issue #258 expected behavior

* chore(ci): add contract parity test script and CI enforcement documentation (task #22)

- Add 'test:contracts' script to package.json for running contract parity tests
- Document CI enforcement in background-agent-contracts.ts JSDoc
- Contract tests automatically run in CI via 'bun test' command
- Lefthook pre-commit hook runs 'bun test --bail' which includes contract tests
- All 116 contract parity tests passing (provider, runtime, acceptance, etc.)

* feat(ui): add mode==='background' detection for Copilot task tool (task #1)

- Add background detection for input.mode === 'background' at line 644 (tool.start handler)
- Add background detection for input.mode === 'background' at line 704 (tool.start handler)
- Add background detection for fallbackInput?.mode === 'background' at line 1091-1093 (subagent.start handler)

This ensures Copilot's built-in task tool mode parameter is properly detected
in addition to the existing run_in_background flag.

* feat(ui): relax subagent.start correlation guard for session-owned events (task #2)

Relaxes the second correlation guard in the subagent.start handler to also
allow session-owned events, not just events with pendingTaskEntry or
sdkCorrelationMatch.

This supports SDKs like Copilot that dispatch custom agents without a Task
tool, by allowing session-owned events during active streaming.

Changes:
- Modified line ~1073 in src/ui/index.ts to include '&& !sessionOwned' check
- Updated comment to explain the rationale for session-owned event allowance

Testing:
- All 1676 tests pass
- No type errors (bun typecheck passes)

* feat(sdk): add toolCallId to OpenCode agent part events for UI correlation

- Add toolCallId field to subagent.start events for agent parts
- Use part.callID as primary correlation ID, fallback to part.id
- Enables SDK correlation in UI layer for agent event tracking
- Matches correlation pattern used in tool parts
- All tests passing (1676 tests)

* feat(sdk): enrich Copilot subagent.started event with toolCallId and task (task #3)

* test(ui): add comprehensive unit tests for subagent.start guard relaxation (task #9)

- Add 35 tests verifying the relaxed correlation guard logic
- Tests cover both guards at lines 1068 and 1073 in src/ui/index.ts
- Verify session-owned events pass through without pendingTaskEntry or SDK correlation
- Verify non-session-owned events without correlation are still blocked
- Add real-world scenario tests for Copilot, Claude, and OpenCode flows
- Add edge case tests and regression tests for existing flows
- All 1711 tests pass including new guard relaxation tests

* feat(sdk): add debug logging for OpenCode event verification (task #5)

Add temporary debug logging at key event emission points in OpenCode SDK client:
- tool.start events: log toolName, toolId, and hasToolInput
- subagent.start from agent parts: log partType, subagentId, subagentType, toolCallId
- subagent.start from subtask parts: log partType, subagentId, subagentType

Debug logging is gated behind process.env.ATOMIC_DEBUG flag.
This enables runtime verification of:
- Whether tool.start fires with correct toolName (Task vs task)
- Whether subagent.start fires from agent/subtask part types
- What fields are present in the event data

No logic changes, only observability improvements for development.

* test(sdk): add comprehensive tests for Copilot subagent event mapping

- Add test for subagent.started → subagent.start with enriched data (toolCallId, task)
- Add test for task fallback priority: description → prompt → agentName
- Add test for subagent.completed → subagent.complete with success: true
- Add test for subagent.failed → subagent.complete with success: false and error
- All 10 tests passing, verifying event mapping logic in copilot.ts

* test(sdk): add comprehensive tests for OpenCode agent event mapping (task #8)

* fix(ui): preserve background agents across interrupt and prevent duplicate agent trees

- Add separateAndInterruptAgents helper to only interrupt foreground agents
  while preserving background agents during Ctrl+C
- Guard mergeParallelAgentsIntoParts to skip when agent parts already exist
  from streaming, preventing duplicate agent tree rendering
- Preserve background agents across resetParallelTracking during interrupt
- Fix background termination (Ctrl+F) to clear agents from state and abort
  SDK session only when not streaming
- Update footer resolver and contracts to use consistent naming conventions
- Update all related tests to match new behavior

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor(ui): use getActiveBackgroundAgents helper for background agent filtering

Replace inline `a.background && a.status === "background"` filter
patterns with the shared getActiveBackgroundAgents utility across all
occurrences in chat.tsx for consistency and maintainability.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: lavaman131 <dev@example.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
lavaman131 pushed a commit that referenced this pull request Mar 26, 2026
lavaman131 added a commit that referenced this pull request Mar 26, 2026
…es (#212)

* feat(ui): add getReadyTasks() dependency filter to task-order

- Add getReadyTasks() exported function for filtering pending tasks
- Returns only tasks whose blockedBy dependencies are all completed
- Reuses normalizeTaskId() for consistent ID handling
- Add comprehensive test suite with 15 new test cases
- All tests pass with 100% function coverage and 99.13% line coverage
- Type-safe and deterministic implementation

Supports DAG orchestration by identifying ready-to-execute tasks.
Completes task #1 from workflow.

* feat(ui): add detectDeadlock() with cycle and error dependency diagnostics

- Add DeadlockDiagnostic type with cycle, error_dependency, and none variants
- Implement detectDeadlock() function that:
  - Detects circular dependencies using DFS algorithm
  - Identifies pending tasks blocked by error tasks
  - Reuses normalizeTaskId() for consistent ID handling
  - Returns detailed diagnostic information
- Add comprehensive test suite with 18 focused test cases covering:
  - Cycle detection (simple, complex, self-referential)
  - Error dependency detection
  - Edge cases (empty lists, invalid IDs, unknown blockers)
  - Priority handling (cycles before error dependencies)
- All 40 tests pass with 99.12% line coverage

* feat(ui): replace serial Ralph worker loop with DAG orchestrator

- Replace serial worker loop in fresh run flow with runDAGOrchestrator call
- Replace serial worker loop in resume flow with runDAGOrchestrator call
- Remove unused imports: buildTaskListPreamble, saveWorkflowSession
- Update test to mock SubagentGraphBridge for DAG orchestrator
- Update test expectations to reflect DAG orchestrator behavior (completes all pending tasks)
- Preserve logging/progress UX and persistence semantics from tasks #6-#12

This change enables parallel task execution while maintaining compatibility with existing workflow state management.

* fix(ui): resolve buildContentSegments regression failures

Fix 5 failing adversarial formatting tests in content segment builder:

- Skip task list insertion when tasksExpanded is false to avoid
  splitting text for hidden/collapsed task panels
- Remove trimStart() on remaining text after tool insertions to
  preserve leading whitespace boundaries
- Restrict paragraph splitting to text truly interleaved between
  non-text segments and skip fenced code blocks

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor(ralph): remove auto orchestrator from run/resume paths

Remove automatic runDAGOrchestrator() invocation from both /ralph run
and resume command paths. After bootstrapping session and task state,
control now returns to the main agent for manual worker dispatch.

- Remove runDAGOrchestrator() function and all orchestrator-only imports
- Update resume test to verify normalized state without auto-completion
- Remove DAG orchestrator integration and E2E test suites (dead code)
- Update module description to reflect manual dispatch model

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor(ralph): remove obsolete orchestrator wiring and imports

Remove dead orchestrator infrastructure from workflow-commands.ts that
was left behind after removing auto orchestrator calls in task #1:

- Remove graph-related imports (CompiledGraph, BaseState, NodeDefinition,
  AtomicWorkflowState, setWorkflowResolver, CompiledSubgraph)
- Simplify WorkflowMetadata interface: remove generic type parameter and
  createWorkflow field (graphs are never executed)
- Remove entire workflow registry and resolution section (~150 lines):
  workflowRegistry, initializeRegistry, getWorkflowFromRegistry,
  resolveWorkflowRef, hasWorkflow, getWorkflowNames,
  refreshWorkflowRegistry
- Remove initializeWorkflowResolver and createWorkflowByName functions
- Remove WORKFLOW_DEFINITIONS export alias
- Simplify BUILTIN_WORKFLOW_DEFINITIONS: remove dummy graph node creation
- Update registerWorkflowCommands to not call initializeWorkflowResolver
- Clean up re-exports in commands/index.ts and ui/index.ts

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(ui): align sub-agent/task streaming with ralph bootstrap

Bootstrap Ralph task context after planning/resume so manual worker dispatch starts with task metadata in-session. Improve tool/sub-agent correlation and content insertion ordering so task lists, agent trees, and tool events render in stable chronological order.

Refactor skill and parallel-agent status indicator helpers, pin Ralph task updates to the panel while restoring inline task rendering elsewhere, and add focused regression tests plus related specs/research docs.

Assistant-model: GitHub Copilot CLI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(fmt,review): format + add review step

* test(ui): add comprehensive background agent lifecycle tests

Add parallel-agent-background-lifecycle.test.ts with 19 tests covering:

Unit Tests (8):
- Agent creation with mode=background/async/sync
- tool.complete skips finalization for background agents
- tool.complete transitions sync agents to completed
- subagent.complete transitions background agents to completed/error
- interrupt sets background agent to interrupted

Integration Tests (11):
- Full background lifecycle: spawn → tool.complete → subagent.complete
- Mixed sync+background agents finalize correctly
- Stream finalization hasActive checks include background agents
- Stream finalization map skips background agents
- Field preservation during transformations
- Edge cases (empty arrays, ID matching, etc.)

All tests pass (19/19). Total test suite: 1084 tests passing.

Context: Tests verify the lifecycle state management changes that prevent
background-mode Task agents from being prematurely marked as completed.

* fix(ui): prevent premature completion of background sub-agents

Extract mode parameter at agent creation time to set status: "background"
and background: true flag for background/async Task agents. Guard all five
finalization sites to skip agents with the background flag, allowing
subagent.complete to be the sole terminal event.

- Agent creation: set background status and flag when mode=background|async
- tool.complete: skip status/currentTool/durationMs update for bg agents
- Cleanup helper: include "background" in active agent check
- Stream finalization (3 paths): include "background" in hasActive check
- Add 19 unit/integration tests for background lifecycle transitions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(ui): render all components inline except Ralph task list panel

- Move compaction summary from outside scrollbox to inside scrollbox
- Remove 'background' from hasActive checks so background agents
  don't block stream completion
- Fix subagent.complete handler to allow background agent updates
- Add backgroundAgentMessageIdRef to track post-stream completion
  updates for background agents in baked messages
- Keep background agents in live state after stream finalization
  so completion events can propagate to the correct message
- Improve task segment rendering with border and progress text
- Fix setMessagesWindowed purity (defer side-effects to useEffect)
- Fix TS errors in background lifecycle tests (Object possibly undefined)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(parts): add ToolState discriminated union type

- Add ToolState type with 5 states: pending, running, completed, error, interrupted
- Enforce state machine: pending → running → (completed|error|interrupted)
- Export ToolState from parts module
- Satisfies spec §5.3 Tool State Machine requirements

* feat(ui): implement useThrottledValue hook for 100ms text debounce

- Create useThrottledValue hook with generic type parameter
- Throttle interval defaults to 100ms
- Uses refs for last update time tracking
- Cleans up pending timeouts on unmount
- Add hook export to hooks index
- Add basic validation tests

Implements task #20 from parts-based rendering spec §5.3

* feat(parts): define all Part type interfaces and Part discriminated union

- Add imports for HitlResponseRecord, PermissionOption, ParallelAgent, TaskItem, MessageSkillLoad, McpSnapshotView, and ContextDisplayInfo
- Define concrete Part type interfaces:
  * TextPart: accumulated text with streaming state
  * ReasoningPart: reasoning content with duration
  * ToolPart: tool execution with state machine and HITL support
  * AgentPart: parallel agent tracking
  * TaskListPart: task list with expansion state
  * SkillLoadPart: skill loading status array
  * McpSnapshotPart: MCP server snapshot view
  * ContextInfoPart: context display information
  * CompactionPart: message compaction summary
- Define Part discriminated union type for all part types
- Export all new types from parts module index

Tasks #3 and #4 complete.

* feat(parts): add optional parts field to ChatMessage interface

- Add Part type import from parts module
- Add optional parts?: Part[] field to ChatMessage interface
- Field placed after streaming field as per spec
- Maintains backward compatibility with optional operator
- Documentation comment added for chronological ordering

Task #6 complete. Unblocks tasks #7, #9, and #16.

* feat(ui): create ReasoningPartDisplay renderer component

- Created src/ui/components/parts/reasoning-part-display.tsx
- Component renders ReasoningPart with thinking emoji and duration
- Displays dimmed text using theme colors (colors.muted)
- Shows 'Thinking...' during streaming, 'Thought (X.Xs)' when complete
- Created src/ui/components/parts/index.ts with exports
- Task #22 complete

* feat(parts): add optional parts field to ChatMessage interface

- Add Part type import from parts module
- Add optional parts?: Part[] field to ChatMessage interface
- Field placed after streaming field as per spec
- Maintains backward compatibility with optional operator
- Documentation comment added for chronological ordering

Task #6 complete. Unblocks tasks #7, #9, and #16.

* test(parts): add unit tests for shouldFinalizeOnToolComplete guard

- Create comprehensive test suite for shouldFinalizeOnToolComplete()
- Test all agent status types (pending, running, completed, error, interrupted, background)
- Test background flag behavior (agent.background = true)
- Test background status behavior (status = 'background')
- All 8 tests pass with 100% code coverage

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(parts): create ToolPartDisplay with inline HITL overlay support

- Created src/ui/components/parts/tool-part-display.tsx
  * Renders ToolPart with tool execution status via ToolResult component
  * Displays active HITL questions inline using UserQuestionInline
  * Shows completed HITL responses as compact records using CompletedHitlDisplay
  * Implements toolStateToStatus() converter from ToolState to ToolExecutionStatus
  * Follows parts-based rendering architecture (spec §5.5)

- Updated src/ui/components/parts/index.ts
  * Added ToolPartDisplay and ToolPartDisplayProps exports

Key architectural changes:
- HITL questions render inline after tool output (not as fixed overlays)
- Uses discriminated union ToolState for tool execution states
- Bridges to existing ToolResult component for consistent tool output rendering
- Supports both pendingQuestion (active) and hitlResponse (completed) states

Implements Task #24 from parts-based rendering specification

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(tests): add TypeScript type assertions for array access safety

- Add non-null assertions for array accesses in id.test.ts and store.test.ts
- Cast Part[] elements to TextPart when accessing content property
- Fixes strict TypeScript checks while maintaining test correctness
- All tests still pass with 100% coverage

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(parts): dual-populate AgentPart on sub-agent start events

Modify the sub-agent update effect in chat.tsx to create/update
AgentPart in message.parts[] alongside the existing parallelAgents
field. This enables parts-based rendering of sub-agents while
maintaining backward compatibility with legacy rendering.

Implementation:
- Import createPartId, upsertPart, and AgentPart type
- Find or create AgentPart in parts[] array during both:
  * Active streaming message updates
  * Background agent completion updates
- Use upsertPart() for sorted insertion/update
- Preserve all existing behavior (dual population pattern)

Testing:
- All 469 existing UI tests pass
- Type checking passes without errors
- No behavior changes to legacy rendering path

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(parts): implement handleTextDelta() for text streaming with tool splits

Implements handleTextDelta() function that handles text streaming with natural
tool boundary splitting. The function:
- Appends to existing streaming TextPart if isStreaming is true
- Creates new TextPart if previous is finalized or doesn't exist
- Naturally handles tool-boundary text splitting

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* test(parts): add unit tests for handleTextDelta and getMessageText

- Add handlers.test.ts with 4 test cases for handleTextDelta
  - Creates new TextPart on empty parts array
  - Appends to existing streaming TextPart
  - Creates new TextPart when last is not streaming
  - Handles undefined parts initialization
- Add helpers.test.ts with 4 test cases for getMessageText
  - Returns empty string for undefined/empty parts
  - Concatenates multiple TextPart contents
  - Ignores non-text parts
- All 8 tests pass with 100% function and line coverage

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(parts): create PART_REGISTRY mapping part types to renderers

- Create src/ui/components/parts/registry.tsx with PART_REGISTRY
- Map all Part types to their corresponding renderer components
- Export PartRenderer type and PART_REGISTRY from index.ts
- Registry enables dynamic dispatch based on Part discriminant

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(parts): dual-populate ToolPart on tool.start events

- Finalize streaming TextPart (set isStreaming: false) when tool starts
- Create new ToolPart with status: running and startedAt timestamp
- ToolPart includes toolCallId, toolName, input from SDK event
- Maintains existing tool start behavior (toolCalls array, offsets, etc.)
- Uses upsertPart() for chronological insertion into parts[] array

Implements Task #14 per spec §5.4 dual-population requirements

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(parts): dual-populate TextPart on text streaming chunks

Modify all three text chunk handlers in chat.tsx to create/update TextPart
alongside the existing legacy content field:

1. onChunk callback (line 2452) - workflow initialization streaming
2. handleChunk (line 3355) - main stream message handler
3. handleChunk (line 4818) - queued message handler

Implementation:
- Import handleTextDelta from parts/handlers.ts
- Call handleTextDelta(msg, chunk) before updating message
- Spread parts array into message update: { ...msg, parts: withParts.parts }
- Existing content accumulation unchanged: content: msg.content + chunk

This implements dual population - the existing code continues to work
exactly as before, but we ALSO populate the parts[] array with TextPart
for the new parts-based rendering system.

Backward Compatible:
- parts field is optional on ChatMessage
- No changes to existing content field behavior
- All 477 existing tests pass

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(parts): apply shouldFinalizeOnToolComplete guard to prevent premature stream completion

Modify the stream finalization logic in index.ts to use the
shouldFinalizeOnToolComplete() guard when checking for active
agents. This prevents the stream from being marked as complete
prematurely when background agents are still running.

The guard returns false for background agents (either via the
background flag or status), ensuring that:
- Background agents can continue running after tool.complete
- Stream remains active until background agents reach terminal state
- subagent.complete events are properly processed

The dual population of AgentPart was already implemented in task #16
via the parallelAgents effect in chat.tsx, so this task focuses on
applying the finalization guard to prevent the critical bug where
background agents cause premature stream completion.

Implementation:
- Import shouldFinalizeOnToolComplete from parts/index.ts
- Update hasActiveAgents check in stream finalization (line 1188)
- Keep stream active if any agent returns false from guard

Testing:
- All 19 background agent lifecycle tests pass
- All 8 shouldFinalizeOnToolComplete guard tests pass
- TypeScript compilation successful

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(parts): create MessageBubbleParts component rendering from parts[]

Implement MessageBubbleParts component that renders ChatMessage using
the parts-based rendering system instead of buildContentSegments().

- Create src/ui/components/parts/message-bubble-parts.tsx
- Export component from parts index.ts
- Component dispatches each part to its renderer via PART_REGISTRY
- Returns null if message has no parts
- Passes isLast flag to indicate final part in sequence

Implements task #29 per spec §5.5.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(parts): add usePartsRendering feature flag toggle

During Phase 3 migration, this defaults to false (legacy rendering).
Toggle via ATOMIC_PARTS_RENDERING environment variable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(parts): add shouldFinalizeOnToolComplete guard to tool.complete handler

Apply the shouldFinalizeOnToolComplete() guard function to all three
finalization paths in the tool.complete handler (index.ts lines 655-732)
to prevent premature stream completion when a background agent's tool
completes.

Also includes Task #18 implementation: Modify handlePermissionRequest to
set pendingQuestion on ToolPart for inline HITL rendering.

Changes (Task #33):
- Replace inline a.background checks with shouldFinalizeOnToolComplete(a)
  guard in the ID-based correlation path (lines 664-676)
- Add shouldFinalizeOnToolComplete(a) check to the fallback path that
  finds the last running agent without a result (line 692)
- Add shouldFinalizeOnToolComplete(a) check to the no-result completion
  path for eager agents (line 725)

Changes (Task #18):
- Update handlePermissionRequest to accept optional toolCallId parameter
- Find matching ToolPart by toolCallId in message.parts[] array
- Set pendingQuestion field on ToolPart with HITL request data
- Preserve existing overlay dialog behavior during dual-population

The guard returns false for background agents (via background flag or
status), ensuring:
- Background agents continue running after tool.complete
- Stream remains active until background agents reach terminal state
- subagent.complete events are properly processed
- Only sync/foreground agents transition to completed on tool.complete

Testing:
- All 19 background agent lifecycle tests pass
- All 55 parts unit tests pass with 100% coverage
- TypeScript compilation successful (no new errors)

Spec reference:
§5.4 Fix 3: Stream Deferral and Finalization Guards

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(parts): complete Task #18 - clear pendingQuestion and set hitlResponse on ToolPart

Complete the permission request handling by updating handleQuestionAnswer
to clear pendingQuestion and set hitlResponse on the matching ToolPart
when the user responds to a HITL question.

Changes:
- Add toolCallId field to PermissionRequestedEventData interface (types.ts)
- Update handleQuestionAnswer to find matching ToolPart by toolCallId
- Clear pendingQuestion field when user responds
- Set hitlResponse field with user's answer
- Maintain dual-population with legacy toolCalls array
- Add comprehensive unit tests for permission request handling

This completes Task #18 implementation started in commit 0eb3136,
which added pendingQuestion setting in handlePermissionRequest.

Testing:
- All 5 permission request tests pass
- Existing HITL tests continue to pass
- TypeScript compilation successful

Spec reference: §5.4 SDK Event → Part Updates (permission.requested)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(ui): add shouldFinalizeOnToolComplete guard to stream finalization effect

Prevent premature stream finalization when background agents are still running.
The guard checks all parallel agents before allowing finalization to proceed,
ensuring background agents complete before the stream is finalized.

This addresses one of the 4+ finalization paths identified in the spec,
complementing the existing guards in index.ts and the tool.complete handler.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* test(parts): add integration tests for dual-population output comparison

Verify the dual-population mechanism produces consistent parts[] data
alongside the legacy content/segments model during the transition period.

Test coverage:
- Text streaming produces TextPart with matching content
- Tool start creates ToolPart and finalizes TextPart
- Tool complete updates ToolPart state transitions
- Tool error updates ToolPart state to error
- Sub-agent creates AgentPart in parts[]
- Permission request sets pendingQuestion on ToolPart
- HITL response clears pendingQuestion and sets hitlResponse
- Multiple text-tool-text sequences create separate parts in order
- AgentPart updates preserve existing parts

Also fix TypeScript strict mode issues:
- Add undefined checks in store.ts for array access operations
- Fix TextPartDisplay to use OpenTUI's fg style prop instead of color
- Remove unused isLast parameter from TextPartDisplay

All 64 parts tests pass with 100% code coverage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(parts): wire feature flag into chat.tsx to switch old/new rendering

- Import usePartsRendering hook and MessageBubbleParts component
- Call usePartsRendering() in MessageBubble component
- Add conditional rendering for assistant messages with parts[]
- Falls back to legacy buildContentSegments() when flag is disabled
- All existing rendering code preserved intact

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* test(parts): add integration tests for HITL inline rendering

Write comprehensive integration tests verifying that HITL (Human-in-the-Loop)
permission requests are correctly represented inline within the parts model,
replacing the old fixed-position overlay approach.

Tests cover:
- Permission request sets pendingQuestion on ToolPart
- HITL response clears pendingQuestion and sets hitlResponse
- Multiple HITL requests on different tools maintain independence
- ToolPart without HITL has no pendingQuestion
- HITL response preserves tool state
- pendingQuestion has all required fields (requestId, header, question, options, multiSelect, respond)
- Multi-select HITL questions with multiple options
- Cancelled/declined HITL responses
- Custom input response mode
- Chat about this response mode

All 10 tests pass. Tests use bun:test framework and follow existing patterns
from the parts model test suite.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* test(parts): add integration tests for HITL inline rendering

Write comprehensive integration tests verifying that HITL (Human-in-the-Loop)
permission requests are correctly represented inline within the parts model,
replacing the old fixed-position overlay approach.

Tests cover:
- Permission request sets pendingQuestion on ToolPart
- HITL response clears pendingQuestion and sets hitlResponse
- Multiple HITL requests on different tools maintain independence
- ToolPart without HITL has no pendingQuestion
- HITL response preserves tool state
- pendingQuestion has all required fields (requestId, header, question, options, multiSelect, respond)
- Multi-select HITL questions with multiple options
- Cancelled/declined HITL responses
- Custom input response mode
- Chat about this response mode

All 10 tests pass. Tests use bun:test framework and follow existing patterns
from the parts model test suite.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* test(parts): write background agent lifecycle tests

Verify that background agents are not prematurely finalized across all
finalization paths. Tests cover shouldFinalizeOnToolComplete() guard
behavior for:
- Background vs foreground agents
- Different agent statuses (running, completed, pending, error, interrupted)
- Mixed agent scenarios
- Edge cases (undefined background flag, both flag and status set)

All 15 tests pass with 100% coverage of guards.ts.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* test(parts): write E2E test for complete message stream render order

Implement comprehensive E2E test suite for verifying complete order of parts
in a message after a full streaming session with text, tools, agents, and HITL
events.

Implementation:
- Created src/ui/parts/stream-order.test.ts with 12 comprehensive test cases
- Tests verify both part types AND chronological ordering via monotonically
  increasing IDs
- Simulates real streaming scenarios with actual handler functions

Test Scenarios:
1. Simple text-only stream: Text deltas → verify single TextPart
2. Text → Tool → Text sequence: Verify [TextPart, ToolPart, TextPart]
3. Text → Tool → HITL → Response → Text: Verify full HITL flow maintains order
4. Text → Multiple tools → Text: Verify [TextPart, ToolPart, ToolPart, TextPart]
5. Agent spawn mid-stream: Text → Agent spawn → Tool in agent
6. Complex realistic scenario: Text → Reasoning → Tool1 (with HITL) → Tool2 → Agent → Text
7. Parts maintain chronological order via IDs: Verify each part.id is lexicographically greater
8. Empty stream produces no parts: Edge case for no streaming events
9. Consecutive reasoning parts maintain order: Multiple reasoning parts in sequence
10. Interleaved text and tool calls: Complex interleaving pattern
11. Background agent does not break ordering: Background agent survives
12. HITL updates preserve tool order: Updates don't change IDs

Key Features:
- Uses bun:test framework
- Tests data flow, not rendering (no React components)
- Helper functions for creating mock messages, parts, agents, and HITL
- verifyMonotonicIds() helper ensures chronological ordering
- 100% code coverage for handlers.ts, id.ts, store.ts
- All 12 tests pass, 133 expect() calls

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* test(parts): add E2E tests for HITL inline position and sticky scroll

Verify that HITL permission requests appear inline at correct positions
within the parts model, not as fixed overlays. Tests cover:

- HITL appearing at correct ToolPart position after text → tool → request
- HITL position is inline with tool (not separate part)
- hitlResponse replacing pendingQuestion at same position
- Multiple sequential HITL requests maintaining correct positions
- HITL on second tool in sequence with first tool completed
- HITL position persisting across message updates and streaming
- Complex scenarios with mixed HITL states across multiple tools
- Order preservation when responding to HITL questions

All 9 tests pass with 100% coverage on handlers, id, and store modules.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* test(parts): write E2E test for message eviction with parts model

Tests verify that the parts model works correctly with message window
eviction (MAX_VISIBLE_MESSAGES = 50 with messageWindowEpoch remount).

Test cases:
- Parts survive message object identity change (shallow copy)
- Parts are serializable (JSON.stringify/parse)
- Large parts array (100+) handles eviction
- Parts maintain order after message copy
- Empty parts array after eviction (graceful handling)
- Parts array is not shared reference across messages

All tests pass with 100% coverage of id.ts functions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor(parts): deprecate buildContentSegments() and ContentSegment type

These legacy rendering functions are replaced by parts-based rendering
via MessageBubbleParts. They will be fully removed after the
usePartsRendering feature flag is removed (Phase 5 cleanup).

Changes:
- Add @deprecated annotation to ContentSegment interface
- Add @deprecated annotation to buildContentSegments() function
- Document replacement: use MessageBubbleParts instead
- Note: will be removed when feature flag is removed

Testing:
- bun test src/ui/parts/ (136 tests pass, 100% coverage)
- bun run typecheck (no errors in chat.tsx)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor(parts): deprecate legacy offset fields in ChatMessage

Add @deprecated annotations to contentOffsetAtStart, agentsContentOffset,
and tasksContentOffset fields. These legacy offset tracking fields will be
removed when the parts-based rendering feature flag is removed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor(parts): deprecate legacy content:string field in ChatMessage

The parts-based model replaces the monolithic content string with structured parts[] array. Mark content field as deprecated while maintaining it for the legacy rendering path and dual-population.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor(parts): deprecate usePartsRendering feature flag

Mark usePartsRendering as temporary migration flag to be removed
once parts-based rendering is fully validated in production.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore(parts): remove unused isLast parameter from ToolPartDisplay

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: exclude vendored docs from typecheck and test discovery

The docs/ directory contains vendored reference code (opencode, opentui)
with unresolved dependencies that fail tsc and bun test. Exclude docs/
from tsconfig and scope test discovery to src/ so pre-commit hooks pass
without requiring vendored dependencies to be installed.

Assistant-model: Claude Code

* fix(ui): filter sub-agent tool calls from main chat display

Sub-agent tool calls (attributed to running parallel agents) were being
dispatched through both the parallel agents tree and the main chat
tool-call handlers, causing duplicate display. Track sub-agent tool IDs
and gate toolStartHandler/toolCompleteHandler so only non-subagent tools
appear in the message parts and ctrl+o transcript.

Assistant-model: Claude Code

* refactor(ui): complete parts-based rendering migration with design system

Replace legacy offset-based buildContentSegments() with parts-driven
getRenderableAssistantParts() that synthesizes tool, agent, task-list,
MCP snapshot, and context-info parts directly from message data.

Key changes:
- Remove buildContentSegments(), ContentSegment, and content offset
  tracking (agentsContentOffset, tasksContentOffset, contentOffsetAtStart)
- Remove usePartsRendering feature flag and hook
- Add SPACING constants and TASK icon set for consistent layout tokens
- Overhaul task list indicator with numbered rows, left rail, progress
  bar, and status labels
- Simplify parallel agents tree (static indicators, remove blink)
- Improve HITL tool rendering with dedicated display path
- Add circle indicator prefix to assistant text parts
- Remove file-content loading from @mention processing (metadata only)
- Delete obsolete tests for buildContentSegments and skill-indicator e2e

Assistant-model: Claude Code

* fix(ui): use run_in_background for background agent detection with isAsync fallback

Switch background agent detection from checking mode="background"|"async"
to checking input.run_in_background === true, aligning with the actual
Task tool API. Add isAsync fallback in parseTaskToolResult to retroactively
mark agents as background when the tool result indicates async execution.

Assistant-model: Claude Code

* refactor(ui): extract TaskListBox as shared presentational component

Split TaskListPanel into a reusable TaskListBox (bordered container with
progress header, bar, and task rows) and a file-driven TaskListPanel
wrapper. TaskListPartDisplay now uses TaskListBox directly. Remove unused
sessionId prop from TaskListPanel.

Assistant-model: Claude Code

* feat(ui): add skill load indicator for builtin skills

Track skill loads in chat messages with session-level deduplication via
loadedSkillsRef. Render SkillLoadPart in assistant message parts for
selected builtin skills (prompt-engineer, frontend-design,
testing-anti-patterns). Also remove now-unused sessionId prop from
TaskListPanel usage.

Assistant-model: Claude Code

* refactor(ui): simplify completed HITL response display

Replace bordered badge style in CompletedHitlDisplay with a compact
single-line format matching ToolResult headers: status icon + label +
question + indented response. Simplify HITL display text for declined
and chat_about_this response modes.

Assistant-model: Claude Code

* feat(sdk): add Skill and MultiEdit to allowed tool names

Assistant-model: Claude Code

* fix(ui): add skill-loaded directive to prevent model re-invocation of expanded skills

Prepend a <skill-loaded> tag when sending expanded builtin skill
prompts so the model acts on the already-expanded content rather than
re-loading the raw skill via the Skill tool. Also clarify in the
capabilities system prompt that listed skills are user-invocable and
the model should use the Skill tool directly.

Assistant-model: Claude Code

* refactor(ui): consolidate part spacing via parent gap instead of per-child margins

Move inter-part spacing responsibility to the parent MessageBubbleParts
container using gap={SPACING.ELEMENT}. Remove marginBottom from child
part components (AgentPartDisplay, CompactionPartDisplay, ToolPartDisplay,
ToolResult) to avoid double-spacing.

Assistant-model: Claude Code

* refactor(ui): simplify task list display and remove maxWidth constraint

Remove zero-padded index numbers from task items and the RUNNING status
label (keep FAILED). Drop the maxWidth prop from TaskListBox and
simplify width calculations. Use conditional scrollbox only when items
exceed the scroll threshold instead of always wrapping in scrollbox.

Assistant-model: Claude Code

* chore(deps): bump sdk and dev dependency versions

Update @anthropic-ai/claude-agent-sdk to ^0.2.44, @github/copilot-sdk
to ^0.1.24, @opencode-ai/sdk to ^1.2.6, @clack/prompts to ^1.0.1,
oxlint to ^1.48.0, and type packages.

Assistant-model: Claude Code

* feat(skills): materialize builtin skills as SKILL.md files for SDK discovery

Write BUILTIN_SKILLS to .claude/skills/<name>/SKILL.md at startup so
each SDK's native skill discovery mechanism (Claude Skill tool, Copilot
skillDirectories, OpenCode server) can find them. Files are only
rewritten when content changes. The generated directory is gitignored.

Assistant-model: Claude Code

* refactor(sdk): add configurable thinking/reasoning effort to Claude client

Add maxThinkingTokens to SessionConfig and ReasoningEffort type with
getReasoningEffort() helper. Thinking mode is now adaptive for opus and
budget-based (defaulting to 16000 tokens) for other models. Also
reformats claude-client.ts to consistent 4-space indentation and line
wrapping.

Assistant-model: Claude Code

* refactor(ui): replace ralph resume with task loop, add markdown rendering to parts, and fix streaming state

Replace the ralph --resume command with an autonomous task loop that
continues dispatching workers until all tasks complete. Thread syntaxStyle
through the parts rendering pipeline so text parts render as <markdown>
and reasoning parts use a dimmed <code filetype="markdown"> variant.
Fix streaming state cleanup (hasRunningToolRef, streamingMeta) on
interrupts, errors, and stream end to prevent spinner hangs. Improve
task list panel with session ID display and blocker sub-lines. Update
research-codebase and create-spec skill prompts with better instructions.

Assistant-model: Claude Code

* chore(agents): add project memory to Claude agents and update worker skill refs

Enable `memory: project` on all Claude agent configs for persistent context.
Remove hardcoded model from OpenCode agents. Update worker.md to reference
the `Skill` tool instead of the removed `SlashCommand` tool.

Assistant-model: Claude Code

* refactor(skills): migrate commands to skills directories with cross-SDK sync

Replace legacy `.claude/commands/` and `.opencode/command/` directories with
unified `.claude/skills/`, `.opencode/skills/`, and `.github/skills/` SKILL.md
files. Add cross-sync materialization so all three SDK directories contain the
full skill catalog. Update init.ts to use `skillsSubfolder` and remove the
per-agent `getCommandsSubfolder` helper. Remove legacy `SKILL_DEFINITIONS`
from skill-commands.ts.

Assistant-model: Claude Code

* fix(ui): improve HITL display, user question styling, and task list rendering

Redesign tool-part-display to show completed HITL responses in a tree
hierarchy with question and answer. Enhance user-question-inline with
header badges, numbered options, and navigation hints. Trim trailing
newlines in text-part-display. Inline blocker info in task-list-indicator
instead of using a separate sub-line. Remove unused sessionId prop from
TaskListPanel. Enable viewportCulling in transcript-view for performance.

Assistant-model: Claude Code

* fix(ui): prevent sub-agent TodoWrite from overwriting ralph task state

Add ralphTaskIdsRef to track known task IDs from the planning phase.
Guard TodoWrite persistence so only updates matching these IDs are
written to tasks.json, preventing sub-agent independent todo lists from
clobbering ralph's persistent task state. Add mergeBlockedBy utility to
preserve task dependency info when agents omit blockedBy in updates.

Assistant-model: Claude Code

* perf(ui): migrate history buffer to NDJSON with append-only writes

Replace JSON array storage with NDJSON (newline-delimited JSON) for the
conversation history buffer. Uses appendFileSync for O(1) writes instead
of read-modify-write. Add in-memory dedup Set to avoid re-reading the
file on each append. Support legacy JSON array migration detection on
read. Batch eviction flushes in chat.tsx. Add extensive test coverage
for windowing lifecycle scenarios (/clear, /compact, Ctrl+O, scale).

Assistant-model: Claude Code

* refactor(ui): clean up command exports and standardize formatting

Remove unused exports from index.ts (initializeCommands, legacy skill
re-exports). Drop 'custom' command category from registry. Remove
backward-compatibility re-export of parseMarkdownFrontmatter from
agent-commands.ts. Standardize indentation in builtin-commands.ts.
Add setRalphTaskIds to test mock context.

Assistant-model: Claude Code

* docs: add research and specs for message truncation and legacy code removal

Add codebase research docs and technical design specs for two planned
efforts: message truncation with dual-view system, and legacy code
removal for skills migration cleanup.

Assistant-model: Claude Code

* chore: remove stale gitignore entries for deleted docs directories

Assistant-model: Claude Code

* feat(ui): auto-collapse older messages to single-line summaries

Replace the manual conversation-collapsed toggle with automatic
collapsing based on recency. Only the last 4 messages (EXPANDED_MESSAGE_COUNT)
render fully; older messages show as collapsed single-line summaries.
Live messages (streaming or with active background agents) are never
collapsed regardless of position.

Add shouldCollapseMessage utility with comprehensive tests.

Assistant-model: Claude Code

---------

Co-authored-by: Developer <dev@example.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
lavaman131 added a commit that referenced this pull request Mar 26, 2026
…265)

* refactor(ui): extract stream pipeline and add background agent management

Extract streaming event handling from the monolithic chat component into
dedicated, testable modules:

- parts/stream-pipeline.ts: unified event reducer for text, thinking,
  tool, HITL, and agent streaming events
- utils/loading-state.ts: completion summary and loading indicator logic
- utils/background-agent-footer.ts: active background agent resolution
- utils/background-agent-termination.ts: Ctrl+F double-press termination
- utils/background-agent-tree-hints.ts: parallel agents header hints
- components/background-agent-footer.tsx: footer status component

Additional fixes:
- Normalize Windows line endings (CRLF) in markdown text handling
- Apply text normalization to task tool result parsing
- Expand guards with hasActiveForegroundAgents and shouldFinalizeDeferredStream

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* docs(research): add branch task breakdown for TUI streaming rendering

Document the grouped issues (#259, #258, #254, #248, #231) being
addressed on the fix/tui-streaming-rendering branch with rationale
for their grouping under the streaming content rendering pipeline.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* docs: track cross-agent E2E dependency blockers

List environment provisioning issues causing test failures for
protocol ordering, claude rendering, unified event parity, copilot
client, and opencode events test suites.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(ui): resolve streaming render inconsistencies

Harden tool completion timing and preserve HITL responses when syncing tool
parts.

Improve streaming output rendering by removing text-part status prefixes,
normalizing reasoning duration labels, and converting markdown task checkboxes
to unicode symbols for reliable TUI display.

Add focused tests covering duration formatting, invalid startedAt handling, and
markdown checkbox normalization.

Assistant-model: GitHub Copilot CLI

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* test(ui): add thinking stream interleaving and handoff integration coverage

Assistant-model: openai/gpt-5.3-codex

* chore: remove resolved issues tracker and debug screenshot

These files were used during development and are no longer needed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(sdk,ui): add thinking source identity tracking to streaming pipeline

Propagate provider-native thinking source keys (block index, reasoning
ID, part ID) through all three SDK clients (Claude, Copilot, OpenCode)
and into the UI streaming pipeline.

- Add thinkingSourceKey to MessageDeltaEventData and stream metadata
- Track thinking source lifecycle (create/update/finalize/drop) with
  diagnostics support
- Validate thinking-meta events against message ID and stream generation
  to prevent stale/cross-source bleed
- Build stable React render keys from reasoning source identity
- Filter pending ask-user questions from message bubble rendering
- Add comprehensive tests for source identity, interleaving, and
  validation across all SDK clients

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* docs: add thinking tag stream grouping research and spec

Add research documents for thinking source identity tracking and
background agents UI, plus the implementation spec for thinking tag
stream grouping.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor: align getBackgroundTerminationDecision with canonical discriminated union type

- Remove old BackgroundTerminationDecision interface from background-agent-termination.ts
- Import and re-export BackgroundTerminationDecision from background-agent-contracts.ts
- Update getBackgroundTerminationDecision to return discriminated union:
  - { action: 'none' } when no active background agents
  - { action: 'warn', message: '...' } on first press
  - { action: 'terminate', message: '...' } on second press
- Update chat.tsx to use new discriminated union pattern
- Update all tests to match new return type
- All tests passing, no type errors

* refactor: align footer resolver and component with canonical contract

- Import and use BACKGROUND_FOOTER_CONTRACT in footer files
- Replace hardcoded 'ctrl+f terminate' with contract value
- Add contract validation tests
- Add test for footer visibility threshold

Tasks #7 + #8 complete. All footer UX now driven by the canonical
contract, eliminating hardcoded behavior.

* feat(telemetry): add background termination tracking and metrics

- Add TuiBackgroundTerminationEvent interface with action, activeAgentCount, interruptedCount
- Add trackBackgroundTermination method to TuiTelemetrySessionTracker
- Track noop/warn/execute counters for session summary
- Include counters in TuiSessionEndEvent and TuiSessionSummary
- Supports observability for Ctrl+F keyboard termination flow

Related to tasks #11 and #12 in workflow

* feat(ui): add structured debug logs for background termination state transitions

- Add console.debug call after decision computation with pressCount and activeAgents
- Add debug log in none/noop branch
- Add debug log in terminate branch with interruptedIds and remainingCount
- Add debug log in warn/armed branch
- All logs prefixed with [background-termination] for filtering
- Uses console.debug for structured logging

Related to task #11 in workflow

* test(ui): add parent callback integration tests for background agent termination

* test(ui): add Ctrl+O non-conflict integration test for background termination

- Create background-agent-keybinding-nonconflict.test.ts
- Verify Ctrl+O (transcript toggle) does NOT conflict with Ctrl+F (termination)
- Verify Ctrl+C (interruption) does NOT conflict with Ctrl+F (termination)
- Test modifier exclusion (Ctrl+Shift+F, Ctrl+Meta+F not detected)
- Comprehensive test of all common Ctrl+key combos (a-z)
- All 8 tests pass with 32 expect() calls

* test(ui): add E2E provider parity matrix tests for background agent contracts

* test(ui): add E2E runtime parity tests for background agent contracts

Add comprehensive test suite verifying background agent contract functions
produce deterministic, consistent results invariant across runtime paths
(dev via 'bun run' vs compiled production binary).

Per spec (specs/background-agents-ui-issue-258-parity-hardening.md), dev
and production runtime paths share startChatUI entry point. Contract
functions are pure JavaScript with no runtime-conditional branching.

Test coverage:
- Contract constants frozen/deterministic (BACKGROUND_FOOTER_CONTRACT,
  BACKGROUND_TREE_HINT_CONTRACT)
- Pure function determinism (getBackgroundTerminationDecision,
  interruptActiveBackgroundAgents, getActiveBackgroundAgents,
  buildParallelAgentsHeaderHint, formatBackgroundAgentFooterStatus)
- Idempotency (multiple calls with same args yield same result)
- No environment-conditional branching (no process.env/import.meta checks)
- Module import stability (all exports accessible with expected types)
- Function signature stability (parameter counts remain consistent)

This is a 'canary' test documenting and enforcing invariance rather
than testing complex logic.

Issue #258 Task #20

* test(ui): add acceptance tests for issue #258 background agent UX contracts

- Create fixture-based acceptance tests at background-agent-acceptance.test.ts
- Validate exact footer text/behavior: 'ctrl+f terminate' hint, agent count visibility
- Validate Ctrl+F double-press flow: warn → terminate → agent termination confirmation
- Validate tree hints: running/complete/default states with exact wording
- Test cross-surface consistency: ctrl+f/ctrl+o references, 'terminate' keyword
- Test UX polish: separator style (·), lowercase keybindings, pluralization
- All 21 acceptance tests pass, providing machine-readable screenshot equivalents
- Tests serve as canonical specification for issue #258 expected behavior

* chore(ci): add contract parity test script and CI enforcement documentation (task #22)

- Add 'test:contracts' script to package.json for running contract parity tests
- Document CI enforcement in background-agent-contracts.ts JSDoc
- Contract tests automatically run in CI via 'bun test' command
- Lefthook pre-commit hook runs 'bun test --bail' which includes contract tests
- All 116 contract parity tests passing (provider, runtime, acceptance, etc.)

* feat(ui): add mode==='background' detection for Copilot task tool (task #1)

- Add background detection for input.mode === 'background' at line 644 (tool.start handler)
- Add background detection for input.mode === 'background' at line 704 (tool.start handler)
- Add background detection for fallbackInput?.mode === 'background' at line 1091-1093 (subagent.start handler)

This ensures Copilot's built-in task tool mode parameter is properly detected
in addition to the existing run_in_background flag.

* feat(ui): relax subagent.start correlation guard for session-owned events (task #2)

Relaxes the second correlation guard in the subagent.start handler to also
allow session-owned events, not just events with pendingTaskEntry or
sdkCorrelationMatch.

This supports SDKs like Copilot that dispatch custom agents without a Task
tool, by allowing session-owned events during active streaming.

Changes:
- Modified line ~1073 in src/ui/index.ts to include '&& !sessionOwned' check
- Updated comment to explain the rationale for session-owned event allowance

Testing:
- All 1676 tests pass
- No type errors (bun typecheck passes)

* feat(sdk): add toolCallId to OpenCode agent part events for UI correlation

- Add toolCallId field to subagent.start events for agent parts
- Use part.callID as primary correlation ID, fallback to part.id
- Enables SDK correlation in UI layer for agent event tracking
- Matches correlation pattern used in tool parts
- All tests passing (1676 tests)

* feat(sdk): enrich Copilot subagent.started event with toolCallId and task (task #3)

* test(ui): add comprehensive unit tests for subagent.start guard relaxation (task #9)

- Add 35 tests verifying the relaxed correlation guard logic
- Tests cover both guards at lines 1068 and 1073 in src/ui/index.ts
- Verify session-owned events pass through without pendingTaskEntry or SDK correlation
- Verify non-session-owned events without correlation are still blocked
- Add real-world scenario tests for Copilot, Claude, and OpenCode flows
- Add edge case tests and regression tests for existing flows
- All 1711 tests pass including new guard relaxation tests

* feat(sdk): add debug logging for OpenCode event verification (task #5)

Add temporary debug logging at key event emission points in OpenCode SDK client:
- tool.start events: log toolName, toolId, and hasToolInput
- subagent.start from agent parts: log partType, subagentId, subagentType, toolCallId
- subagent.start from subtask parts: log partType, subagentId, subagentType

Debug logging is gated behind process.env.ATOMIC_DEBUG flag.
This enables runtime verification of:
- Whether tool.start fires with correct toolName (Task vs task)
- Whether subagent.start fires from agent/subtask part types
- What fields are present in the event data

No logic changes, only observability improvements for development.

* test(sdk): add comprehensive tests for Copilot subagent event mapping

- Add test for subagent.started → subagent.start with enriched data (toolCallId, task)
- Add test for task fallback priority: description → prompt → agentName
- Add test for subagent.completed → subagent.complete with success: true
- Add test for subagent.failed → subagent.complete with success: false and error
- All 10 tests passing, verifying event mapping logic in copilot.ts

* test(sdk): add comprehensive tests for OpenCode agent event mapping (task #8)

* fix(ui): preserve background agents across interrupt and prevent duplicate agent trees

- Add separateAndInterruptAgents helper to only interrupt foreground agents
  while preserving background agents during Ctrl+C
- Guard mergeParallelAgentsIntoParts to skip when agent parts already exist
  from streaming, preventing duplicate agent tree rendering
- Preserve background agents across resetParallelTracking during interrupt
- Fix background termination (Ctrl+F) to clear agents from state and abort
  SDK session only when not streaming
- Update footer resolver and contracts to use consistent naming conventions
- Update all related tests to match new behavior

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor(ui): use getActiveBackgroundAgents helper for background agent filtering

Replace inline `a.background && a.status === "background"` filter
patterns with the shared getActiveBackgroundAgents utility across all
occurrences in chat.tsx for consistency and maintainability.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: lavaman131 <dev@example.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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