Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
e209ed9
Define the chat TUI redesign contract
Aaronontheweb Aug 11, 2026
23b76a0
Implement the chat TUI redesign
Aaronontheweb Aug 11, 2026
15588d4
Polish the chat status and Inspector
Aaronontheweb Aug 11, 2026
c3c5712
Complete rich chat event presentation
Aaronontheweb Aug 12, 2026
63bf6fd
Report modified Enter capability in chat
Aaronontheweb Aug 12, 2026
96fb424
Use Termina 0.17.0-beta.4
Aaronontheweb Aug 12, 2026
06c552a
Record completed input and scroll tasks
Aaronontheweb Aug 12, 2026
2fed07c
Prove prompt history recall
Aaronontheweb Aug 12, 2026
293d872
Stabilize approval detail screen proof
Aaronontheweb Aug 12, 2026
b28a4ed
Define a quiet chat visual grammar
Aaronontheweb Aug 12, 2026
f1f87cf
Apply the quiet chat visual grammar
Aaronontheweb Aug 12, 2026
7e6a5bc
Align chat surfaces with the visual grammar
Aaronontheweb Aug 12, 2026
2cdac30
State the final chat viewport rules
Aaronontheweb Aug 12, 2026
c2e1aaa
Assert the Inspector wrap invariant
Aaronontheweb Aug 12, 2026
3267f41
Stabilize the Inspector frame proof
Aaronontheweb Aug 12, 2026
14f2cd1
fix(chat): keep the composer active and show reply deltas
Aaronontheweb Aug 12, 2026
c894e90
docs(chat): add the Quiet Reply Stack review site
Aaronontheweb Aug 12, 2026
5cd081f
docs(chat): stabilize the pulse line width
Aaronontheweb Aug 12, 2026
d409249
fix(chat): label double Escape as x2
Aaronontheweb Aug 12, 2026
c5bbe8c
feat(chat): surface tool rationales in work trace
Aaronontheweb Aug 12, 2026
bf81a72
feat(chat): keep turn work in one reply block
Aaronontheweb Aug 12, 2026
08500c1
feat(chat): queue parallel approval decisions
Aaronontheweb Aug 12, 2026
79c5831
test(chat): await the complete approval detail
Aaronontheweb Aug 12, 2026
20a92a3
spec(chat): correct the active-turn contract
Aaronontheweb Aug 13, 2026
a837d55
fix(chat): preserve the queued prompt batch
Aaronontheweb Aug 13, 2026
0a30288
fix(tools): require a rationale before execution
Aaronontheweb Aug 13, 2026
f79dfe1
test(chat): verify settled block spacing
Aaronontheweb Aug 13, 2026
9839c7b
fix(chat): preserve the thinking pulse for queued prompts
Aaronontheweb Aug 13, 2026
1d9e976
fix(chat): report inline host failures
Aaronontheweb Aug 13, 2026
73eb7b7
test(approvals): align typed policy fixtures
Aaronontheweb Aug 13, 2026
51bfd33
fix(chat): preserve queued prompt batches
Aaronontheweb Aug 13, 2026
a7cad77
fix(evals): report the built binary version
Aaronontheweb Aug 13, 2026
9d507d3
fix(evals): read the daemon log by UTC date
Aaronontheweb Aug 13, 2026
8aaf70c
fix(evals): parse recall counts with portable awk
Aaronontheweb Aug 13, 2026
fc2e230
feat(chat): show the queued message lifecycle
Aaronontheweb Aug 13, 2026
437f2db
fix(chat): bound invalid rationale retries
Aaronontheweb Aug 13, 2026
603fc1e
feat(chat): add tail follow controls
Aaronontheweb Aug 13, 2026
5dc5877
fix(chat): preserve implicit tail follow
Aaronontheweb Aug 13, 2026
00a011a
test(actors): honor required tool rationales
Aaronontheweb Aug 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
<PackageVersion Include="Cronos" Version="0.13.0" />
<PackageVersion Include="Netclaw.SkillClient" Version="0.4.1" />
<PackageVersion Include="ShellSyntaxTree" Version="0.3.3" />
<PackageVersion Include="Termina" Version="0.16.1" />
<PackageVersion Include="Termina" Version="0.17.0-beta.6" />
</ItemGroup>
<!-- Serialization -->
<ItemGroup>
Expand Down Expand Up @@ -142,4 +142,4 @@
<ItemGroup>
<NuGetAuditSuppress Include="https://github.com/advisories/GHSA-2m69-gcr7-jv3q" />
</ItemGroup>
</Project>
</Project>
27 changes: 27 additions & 0 deletions IMPLEMENTATION_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,33 @@ the smallest repeatable manual script plus expected output.

## NOW

### Priority: Redesign The Developer Chat TUI

**PRDs:** `docs/prd/PRD-004-cli-onboarding-and-config.md`, `docs/prd/PRD-009-input-adapters-and-unified-input.md`
**Specs:** `docs/spec/SPEC-002-session-lifecycle-and-protocol.md`, `docs/spec/SPEC-004-cli-contract.md`, `docs/spec/SPEC-010-testing-and-smoke-strategy.md`, `docs/spec/SPEC-011-daemon-architecture.md`
**OpenSpec:** `openspec/changes/redesign-netclaw-chat-tui/`
**Surface area:** session output, SignalR, Termina, chat command, resume, input, approvals, copy
**Verification:** L3 plus the Termina cross-platform matrix

The user promoted this work into `NOW`. The chat command must expose the
daemon's structured event model through a clear developer interface.

The design is extend-only. Existing protocol fields, enum values, presentation
modes, and non-chat applications keep their current behavior and defaults.

Done when:

- [ ] Termina adds an opt-in inline mode while full-screen mode stays the
default.
- [ ] Netclaw shows thoughts, parallel tools, sub-agents, approvals, file
changes, errors, usage, and compaction as structured events.
- [ ] Structured resume data extends `RecentMessages` without removing it.
- [ ] The composer supports `Shift+Enter`, draft restoration, and double
Escape.
- [ ] Native terminal scrollback, mouse-wheel input, semantic copy, and the
inspector pass their defined proof matrix.
- [ ] Headless tests and native smoke tests prove the critical chat flows.

### Priority: Keep MCP HTTP Protocol Fallback Deterministic

**PRD:** `docs/prd/PRD-006-mcp-tool-integration.md`
Expand Down
40 changes: 29 additions & 11 deletions docs/prd/PRD-004-cli-onboarding-and-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
offline vs daemon-required command categorization)
- Revised: 2026-05-24 (bootstrap-only `init`, domain-oriented `config`,
init-owned identity re-entry, explicit reset flow)
- Revised: 2026-08-11 (inline chat, structured output, input, approval, and copy contracts)
- Depends on: `PRD-001`, `PRD-002`

## Goal
Expand Down Expand Up @@ -38,7 +39,7 @@ Netclaw ships as two binaries (see PRD-001 for full architecture):

- **Simple arg routing** in `Program.cs` for command selection (Cocona is archived
as of Dec 2025 — replaced with direct `args[0]` routing)
- **Termina 0.5.1** for interactive TUI commands (`netclaw init`, `netclaw chat`)
- **Termina** for interactive TUI commands, with an explicit mode for each application
- All other commands use plain console output
- Commands that need the daemon connect via `Microsoft.AspNetCore.SignalR.Client`
- If the daemon isn't running and a command requires it, print an error with
Expand Down Expand Up @@ -133,7 +134,8 @@ Command ownership stays explicit:
daemon over SignalR. Renders `SessionOutput` stream, sends `ChannelInput`.
Session entity key: `tui/{uuid}`. If `netclaw.json` is absent, the command
SHALL fail before contacting the daemon with
`daemon not configured - please run netclaw init`. See TUI-001 wireframes.
`daemon not configured - please run netclaw init`. Chat SHALL use the primary
terminal buffer after its full-screen session picker exits. See TUI-001.

### TUI-Interactive Commands (Termina, offline)

Expand Down Expand Up @@ -297,11 +299,13 @@ Results are persisted to the environment inventory file.

### CLI-010 TUI Commands

`netclaw init`, `netclaw config`, and `netclaw chat` SHALL use Termina 0.5.1
for interactive TUI rendering. Bare `netclaw provider` and `netclaw model`
SHALL also use Termina. All other commands SHALL use plain console output. TUI
commands SHALL launch Termina as a hosted service within the mode-selected host
builder.
`netclaw init`, `netclaw config`, and `netclaw chat` SHALL use the pinned Termina
package. Bare `netclaw provider` and `netclaw model` SHALL also use Termina. All
other commands SHALL use plain console output.

Each Termina application SHALL select one presentation mode for its lifetime.
Chat SHALL select `Inline` and `NativeTerminal`. Setup, config, provider, model,
and session picker applications SHALL retain `FullScreen`.

### CLI-011 Chat Thin Client

Expand All @@ -312,12 +316,26 @@ interactive TUI for agent conversations. The TUI SHALL:
- Create a session via the hub and receive a session ID
- Send `ChannelInput` messages via SignalR
- Subscribe to `SessionOutput` stream for rendering
- Render session output as streaming text via StreamingTextNode
- Display tool invocation status inline (completed with duration, in-progress
with spinner)
- Show model name, token usage, and context percentage in status bar
- Reduce each typed output into immutable settled content and a bounded live deck
- Show thought, parallel tool, sub-agent, approval, file, error, usage,
compaction, and turn outcome forms with stable identities
- Keep the settled transcript borderless in native terminal scrollback
- Show model name, token usage, and context percentage in the Session Header
- Use bare `Enter` to submit and `Shift+Enter` to add a newline
- Keep the Composer active while the agent works
- Show every later prompt in the ordered Queue Shelf
- Send all later prompts through the session queue for one FIFO follow-up model call
- Show assistant text as each streaming delta arrives
- Restore a saved draft after prompt history reaches its newest entry
- Clear prompt text only after two Escape keys inside a `TimeProvider` window
- Give a pending approval input priority and preserve `Ctrl+O` detail expansion
- Provide an Inspector and semantic copy for complete safe event detail
- Report copy, terminal, and unsupported-event failures visibly
- Print a clear error if the daemon is not running

The client SHALL preserve `RecentMessages` compatibility. It SHALL prefer the
additive structured transcript when the daemon supplies it.

### CLI-012 Daemon Management

The CLI SHALL provide commands to manage the daemon lifecycle:
Expand Down
17 changes: 14 additions & 3 deletions docs/prd/PRD-009-input-adapters-and-unified-input.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- Owner: Netclaw engineering
- Date: 2026-02-21
- Revised: 2026-02-23 (daemon + thin client split, TUI as SignalR client)
- Revised: 2026-08-11 (correlated activity and structured resume output)
- Depends on: `PRD-001`, `PRD-002`, `PRD-008`

## Goal
Expand Down Expand Up @@ -53,9 +54,11 @@ child session actor.
- Receives keyboard input via Termina TextInputNode
- Sends `ChannelInput` to daemon over SignalR
- Subscribes to `SessionOutput` stream over SignalR for rendering
- Renders responses as streaming text via StreamingTextNode
- Displays tool invocation status inline (name, duration, spinner)
- Shows model name, token usage, and context percentage in status bar
- Reduces typed output into settled transcript blocks and one bounded live deck
- Correlates parallel tools by `CallId` and sub-agents by `RunId`
- Shows thought, approval, file, error, usage, compaction, and turn outcome data
- Restores settled event chronology from the structured resume contract
- Keeps transient tool progress outside model context and persisted history

**Slack Socket Mode Adapter** (Phase 1):
- Runs in-process within the daemon
Expand Down Expand Up @@ -167,6 +170,14 @@ The TUI adapter is a pure thin client running in the `Netclaw.Cli` binary —
all agent logic lives in the daemon. The TUI adapter SHALL display tool
invocation status inline between user message and response.

The output contract SHALL preserve all security-safe typed fields across
SignalR. Tool activity SHALL retain `CallId` and turn identity. Sub-agent
activity SHALL retain `RunId` and parent `CallId`.

The daemon SHALL emit both the current `RecentMessages` field and an additive
structured settled transcript during the compatibility period. The TUI SHALL
not restore an old settled record as active work.

## Acceptance Criteria (MVP)

1. TUI adapter receives input, routes through session actor, renders streaming
Expand Down
43 changes: 40 additions & 3 deletions docs/spec/SPEC-002-session-lifecycle-and-protocol.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# SPEC-002: Session Lifecycle and Protocol

Source PRDs: `PRD-001`
Source PRDs: `PRD-001`, `PRD-009`
Research: `docs/research/context-management-patterns.md`

## Purpose
Expand Down Expand Up @@ -106,6 +106,19 @@ always delivered regardless of filter.
`UsageOutput` includes `ContextWindowTokens` and `UsagePercent` so subscribers
can display context consumption without duplicating session config.

`ToolActivityOutput` carries a stable `CallId`, turn identity, safe phase, and
safe summary. A terminal tool result uses the same `CallId`.

`SubAgentOutput` carries an additive `RunId` and parent `CallId`. Start,
activity, and completion output for one run uses the same identities.

Tool and sub-agent activity uses `OutputFilter.ToolCalls`. Thought activity
uses `OutputFilter.Thinking`. Transient activity does not enter model context
or the actor journal.

Every supported output field has an explicit SignalR mapper disposition. A
mapper test fails when a new field lacks mapped or security-omitted handling.

## Behavior States

```
Expand Down Expand Up @@ -184,7 +197,31 @@ are summarized as "Used {tool} for {purpose} → {outcome}".
| `SessionTitleSet` | Title generated or updated |
| `SessionCompacted` | History compacted with summary + retained messages |

## Structured Resume Timeline

The session keeps a bounded settled timeline for the recent turn window. The
timeline uses framework-owned records with stable discriminators.

The timeline can contain these settled entries:

- user and assistant text
- disclosed thought summary
- tool call and result
- sub-agent result
- file metadata
- error and usage detail
- compaction detail
- approval and turn outcome

`SessionJoined` keeps `RecentMessages`. It adds a nullable `RecentTranscript`.
The daemon emits both fields during the compatibility period.

New readers prefer `RecentTranscript` when present. An absent timeline selects
an explicit legacy conversion path. Unsupported legacy detail produces a
diagnostic entry and never creates a false active state.

## Snapshot

`SessionSnapshot` captures `History`, `TurnCount`, `Title` for fast recovery.
Taken periodically per `SessionConfig.SnapshotInterval` and after compaction.
`SessionSnapshot` captures `History`, `TurnCount`, `Title`, and the additive
settled timeline for fast recovery. New Protobuf tags preserve current tags.
Snapshots occur per `SessionConfig.SnapshotInterval` and after compaction.
26 changes: 26 additions & 0 deletions docs/spec/SPEC-004-cli-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,31 @@ Behavior:
- smoke test command runs optional live integration checks outside CI-required
test suite

### 7) Interactive Chat

- `netclaw chat [--session <id>]`

Behavior:

- starts a Termina application with `Inline` presentation
- selects `NativeTerminal` scroll input
- leaves settled output in the primary terminal buffer
- exits any full-screen session picker before chat starts
- fails visibly when inline mode cannot start
- never selects full-screen chat as a silent fallback

Setup, config, provider, model, and session picker applications retain
`FullScreen` presentation.

The chat composer uses bare `Enter` for submit and `Shift+Enter` for a newline.
A model call does not disable or hide the composer. Later prompts enter the
session actor queue while the current turn runs. The actor retains all accepted
prompts in FIFO order and includes them in one follow-up model call. The client
does not start one turn for each queued prompt. The live region shows assistant
text as each stream delta arrives.
A pending approval owns input before the composer. `Ctrl+O` changes approval
detail without a decision.

## Output and Exit Codes

- default output: human readable text
Expand All @@ -110,6 +135,7 @@ Behavior:
- read-only default for all inspection commands
- mutating commands require explicit confirmation or `--yes`
- no command may silently broaden exposure policy
- no TUI command may silently change its terminal presentation mode

## Onboarding State Persistence

Expand Down
31 changes: 31 additions & 0 deletions docs/spec/SPEC-010-testing-and-smoke-strategy.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,36 @@ tests can validate real provider integrations.
- explicit opt-in tests using real endpoints (for example, local Ollama)
- intended for developer or pre-release validation

## Chat TUI Proof Matrix

The chat redesign requires deterministic headless proof and visual review.
Unit tests alone cannot validate the visual grammar.

Headless tests SHALL cover:

- every supported `SessionOutput` disposition
- parallel tools that finish out of order
- same-name sub-agents with distinct `RunId` values
- structured resume and old payload conversion
- layouts at 40, 60, 80, and 120 columns
- `Shift+Enter`, history draft restore, and double Escape with virtual time
- approval priority, `Ctrl+O`, detail scroll, paste, and semantic copy

Development review SHALL use three disposable video checkpoints outside the
repository. The checkpoints SHALL cover the core chat, rich activity with
approval, and the Inspector with responsive layout.

Each checkpoint SHALL produce a temporary video and selected lossless frame
images. A developer SHALL review these files and record material visual defects.
The tapes SHALL not enter CI or the permanent smoke suite.

The Termina package SHALL provide separate primary-buffer proof for Linux,
macOS, Windows Terminal, and tmux. The proof SHALL cover resize, paste,
selection, scrollback, and exit recovery.

The full-screen smoke suite SHALL prove that init, config, provider, model, and
picker applications retain their current terminal lifecycle.

## Critical Producer/Consumer Contract Inventory

The contracts below are the minimum cross-boundary producer/consumer pairs that
Expand All @@ -45,6 +75,7 @@ proof is not complete yet, the gap is assigned to an explicit `NOW` task in
| Scheduler -> delivery gateway | `SetReminderTool` and reminder persistence write `ReminderDefinition.Delivery` and later emit trusted delivery messages | Reminder execution actor and provider session binding actors that deliver without re-running inbound ACL | `Delivery.Kind` is `Channel` for channel delivery, `Delivery.Transport` is the lowercase provider key such as `slack`, and `Delivery.Address` is a canonical provider channel/user ID resolved before persistence. Runtime trusted delivery uses the stored target rather than a display name. | `src/Netclaw.Daemon.Tests/Reminder/ReminderTargetResolutionPathTests.cs` proves display target resolution to canonical channel/user IDs and unresolved target rejection. `src/Netclaw.Actors.Tests/Reminders/ReminderExecutionActorTests.cs` proves delivery success/failure reporting. Full gateway-chain and no-inbound-ACL re-entry coverage remains an explicit gap in Task 5.3. |
| Tool schemas -> model/tool dispatcher | Built-in tool registrations and MCP tool adapters expose tool declarations and schemas | Provider serializers, `SessionToolExecutionPipeline`, `McpToolAdapter`, and MCP client manager | Model-facing tools serialize as OpenAI-compatible function tools with stable names, descriptions, JSON Schema parameters, and required fields. MCP tool names use `server/tool`. Dispatcher arguments preserve schema-declared string values and reconstruct structured JSON values only when the schema requires them. | `src/Netclaw.Daemon.Tests/Configuration/OpenAiCompatibleChatClientTests.cs` proves OpenAI function-tool serialization and tool-call history shape. `src/Netclaw.Daemon.Tests/Mcp/SmokeMcpServerArgumentCoercionTests.cs` proves schema-driven MCP argument reconstruction over the real stdio JSON-RPC path. Approval allow/deny/prompt and malformed metadata coverage remains an explicit gap in Task 4.2. |
| Memory persistence -> prompt assembly | Memory curation, SQLite memory store, session events, and compaction events persist memory and conversation state | `SQLiteMemoryRecallCoordinator`, `SessionMessageAssembler`, and system prompt/session state assembly | Persisted memory uses framework-owned SQLite records and wire enum strings such as trust audience wire values. Session history uses `SerializableChatMessage` records, not provider SDK chat types. Recall appears as volatile context/nudges and does not mutate the stable system prompt prefix. | `src/Netclaw.Actors.Tests/Memory/SQLiteMemoryStoreTests.cs` proves memory persistence/search filtering and audience boundaries. `src/Netclaw.Actors.Tests/Memory/MemoryRedesignedEvalSuiteTests.cs` proves formation -> persistence -> recall. `src/Netclaw.Actors.Tests/Sessions/SessionMessageAssemblerTests.cs`, `SessionStateTests.cs`, and `src/Netclaw.Actors.Tests/Protocol/SerializationRoundTripTests.cs` prove prompt assembly placement and serialization-safe session records. Restart/recovery and corrupt/missing state coverage remains an explicit gap in Task 5.2. |
| Session output -> SignalR -> chat reducer | Session actor output relay and `SessionOutputMapper` | `DaemonClient`, chat presentation reducer, and inline output owner | Typed output keeps all security-safe fields. Tools use `CallId`. Sub-agents use `RunId` and parent `CallId`. Resume uses settled framework-owned transcript entries. | The `redesign-netclaw-chat-tui` change requires output parity, legacy payload, reducer, and native chat tests before completion. |

## CI Rules

Expand Down
27 changes: 20 additions & 7 deletions docs/spec/SPEC-011-daemon-architecture.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# SPEC-011: Daemon Architecture and Process Model

Source PRDs: `PRD-001`, `PRD-002`, `PRD-004`
Source PRDs: `PRD-001`, `PRD-002`, `PRD-004`, `PRD-009`

## Purpose

Expand Down Expand Up @@ -135,6 +135,10 @@ ReceiveOutput(output: SessionOutputDto) → void

`SessionOutputDto` is a wire-safe mapping of the `SessionOutput` discriminated
union. The mapper handles union → flat DTO conversion for SignalR serialization.
It preserves every security-safe output field and all correlation identities.

The DTO keeps `RecentMessages`. It adds nullable structured transcript and
activity fields. Old clients ignore the additive properties.

### Connection Lifecycle

Expand Down Expand Up @@ -300,13 +304,22 @@ SignalR Client Adapter
Daemon SignalR Hub (/hub/session)
```

The `ChatViewModel` interface remains the same as the current in-process
implementation — it exposes `IObservable<SessionOutput>` and accepts
`SubmitAsync(text)`. The only change is the backend: SignalR client instead of
direct `SessionPipeline`.
`ChatViewModel` exposes the SignalR output stream and accepts prompt submission.
It also owns immutable presentation state for stable event identities.

`ChatPage` uses these semantic regions: Session Header, Transcript, Activity
Rail, Decision Gate, Composer, and Status Line. It does not flatten typed output
into one mutable text value.

The chat process uses Termina inline presentation and native terminal scroll.
The session picker remains a separate full-screen Termina application. The
picker exits before the inline application starts.

All chat output uses the inline output owner. Diagnostic logs use the configured
file or structured sink while the live region is active.

`ChatPage` does not change at all. Same rendering, same paste debounce, same
status bar, same tool call spinners.
The daemon keeps transient tool and sub-agent activity outside persistence. It
persists only framework-owned settled transcript records with additive tags.

## Tool Execution Model

Expand Down
2 changes: 2 additions & 0 deletions docs/ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ This directory contains management UI planning artifacts for Netclaw.
component behavior
- `TUI-001-command-wireframes.md` - Termina TUI wireframes for `netclaw init`,
`netclaw chat`, and plain CLI commands
- `chat-reply-stack-v2/index.html` - interactive Netclaw chat hierarchy,
state, motion, and responsive mock-ups
- `ops-console-v1.html` - static high-fidelity mockup for visual direction

## Design Intent
Expand Down
Loading
Loading