add the team chat inbox over the broker

Renders broker traffic as a chat timeline and makes the user a first-class
participant. The broker grows a Stream<TeamMessage> and a recipient field,
auto-registers a virtual `user` member, and gains sendAsUser. A Flutter-free
TeamChatModel (owned by the orchestrator) accumulates the feed and exposes
postAsUser with @-routing (a new at_commands helper mirroring slash) and an
optional interrupt that cancels the target's turn before delivery. One model
backs two surfaces: a compact cockpit widget that pops out into a full
workspace chat pane. CLI parity via clide.team-chat.open / .post.

T-180.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-05-31 10:49:04 +02:00
co-authored by Claude
parent e3c0b0146b
commit a0636c2e5a
17 changed files with 1656 additions and 31 deletions
@@ -19,6 +19,7 @@ import 'package:clide/builtin/claude/src/conversation_controller.dart';
import 'package:clide/builtin/claude/src/session_naming.dart';
import 'package:clide/builtin/claude/src/stream_json_session.dart';
import 'package:clide/builtin/claude/src/team_broker.dart';
import 'package:clide/builtin/claude/src/team_chat_model.dart';
import 'package:clide/builtin/claude/src/transcript_reader.dart';
import 'package:flutter/foundation.dart';
@@ -146,7 +147,12 @@ class ManagedSession {
ClaudeSessionOrchestrator? activeSessionOrchestrator;
class ClaudeSessionOrchestrator extends ChangeNotifier {
ClaudeSessionOrchestrator({ProcessFactory? processFactory}) : _factory = processFactory ?? _spawnClaude;
ClaudeSessionOrchestrator({ProcessFactory? processFactory}) : _factory = processFactory ?? _spawnClaude {
_chatModel = TeamChatModel(
broker: broker,
sessionResolver: (name) => byMemberName(name)?.session,
);
}
final ProcessFactory _factory;
final _sessions = <String, ManagedSession>{};
@@ -156,6 +162,13 @@ class ClaudeSessionOrchestrator extends ChangeNotifier {
/// session's next turn (T-170).
late final TeamBroker broker = TeamBroker(deliver: _deliverToSession);
/// The shared chat timeline and user-post logic (T-180). Both the compact
/// sidebar widget and the full workspace pane read from this model.
late final TeamChatModel _chatModel;
/// Exposes the shared chat model to widgets and panes.
TeamChatModel get chatModel => _chatModel;
void _deliverToSession(String toId, String text) => _sessions[toId]?.session.send(text);
static Future<StreamJsonProcess> _spawnClaude({required List<String> sessionArgs, required String cwd, Map<String, String>? env}) =>
@@ -328,6 +341,8 @@ class ClaudeSessionOrchestrator extends ChangeNotifier {
m.conversation.dispose();
}
_sessions.clear();
_chatModel.dispose();
broker.dispose();
super.dispose();
}
}