capture a forked session's real id from its init event
A --fork-session branch is spawned without --session-id, so claude mints a new session id that only arrives in the init event; the ManagedSession was left holding its placeholder. StreamJsonSession now captures session_id from the first event that carries it and exposes it via claudeSessionId / sessionIdResolved; the orchestrator folds that back into ManagedSession.sessionId (idempotent for normal sessions). A fork can now itself be resumed or forked. T-185. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -108,10 +108,11 @@ class ManagedSession {
|
||||
final String id;
|
||||
final String role;
|
||||
|
||||
/// The clide-internal session id. For fork sessions this is the placeholder
|
||||
/// UUID passed via [SpawnSpec.sessionId]; the real claude-assigned session id
|
||||
/// arrives in the `init` event and is not yet captured here (T-172 follow-up).
|
||||
final String sessionId;
|
||||
/// The claude session id. Starts as [SpawnSpec.sessionId] (the `--session-id`
|
||||
/// we passed, or a placeholder for a `--fork-session` branch) and is updated
|
||||
/// to the real claude-assigned id once the session's `init` event resolves it
|
||||
/// (T-185) — so a fork exposes the branch's actual id, not the placeholder.
|
||||
String sessionId;
|
||||
|
||||
/// The working directory this session was spawned in. Retained so forks and
|
||||
/// the UI can reference the source context (T-172).
|
||||
@@ -226,6 +227,15 @@ class ClaudeSessionOrchestrator extends ChangeNotifier {
|
||||
forkSourceSessionId: spec.forkSourceSessionId,
|
||||
);
|
||||
_sessions[spec.id] = managed;
|
||||
// Fold the real claude-assigned session id back in once the init event
|
||||
// resolves it (T-185) — matters for forks, whose sessionId starts as a
|
||||
// placeholder. Idempotent for normal sessions (same id we passed).
|
||||
session.sessionIdResolved.listen((id) {
|
||||
if (managed.sessionId != id) {
|
||||
managed.sessionId = id;
|
||||
notifyListeners();
|
||||
}
|
||||
});
|
||||
notifyListeners();
|
||||
return managed;
|
||||
}
|
||||
|
||||
@@ -203,8 +203,10 @@ class StreamJsonSession {
|
||||
final List<McpServer> _mcpServers;
|
||||
final _items = StreamController<ConversationItem>.broadcast();
|
||||
final _statusCtl = StreamController<SessionStatus>.broadcast();
|
||||
final _sessionIdCtl = StreamController<String>.broadcast();
|
||||
StreamSubscription<String>? _sub;
|
||||
SessionStatus _status = const SessionStatus();
|
||||
String? _claudeSessionId;
|
||||
int _localSeq = 0;
|
||||
|
||||
/// Token-by-token streaming state (T-168, wire shape verified by T-184).
|
||||
@@ -272,6 +274,16 @@ class StreamJsonSession {
|
||||
/// of the user's own messages).
|
||||
Stream<ConversationItem> get items => _items.stream;
|
||||
|
||||
/// The claude-assigned session id, resolved from the first event that carries
|
||||
/// `session_id` (the `init` event). For a session started with `--session-id`
|
||||
/// this equals the id we passed; for a `--fork-session` branch (T-185) it is
|
||||
/// the NEW id claude minted, which the orchestrator folds back into the
|
||||
/// [ManagedSession]. Null until the init event arrives.
|
||||
String? get claudeSessionId => _claudeSessionId;
|
||||
|
||||
/// Fires once with the resolved [claudeSessionId] when the init event lands.
|
||||
Stream<String> get sessionIdResolved => _sessionIdCtl.stream;
|
||||
|
||||
/// Session status (model / permission-mode / context tokens), on change.
|
||||
Stream<SessionStatus> get statusStream => _statusCtl.stream;
|
||||
|
||||
@@ -306,6 +318,16 @@ class StreamJsonSession {
|
||||
} catch (_) {
|
||||
return;
|
||||
}
|
||||
// Capture the claude-assigned session id from the first event that carries
|
||||
// it (the init event). For a --fork-session branch this is the NEW id, which
|
||||
// the orchestrator folds back into the ManagedSession (T-185).
|
||||
if (_claudeSessionId == null) {
|
||||
final sid = ev['session_id'];
|
||||
if (sid is String && sid.isNotEmpty) {
|
||||
_claudeSessionId = sid;
|
||||
_sessionIdCtl.add(sid);
|
||||
}
|
||||
}
|
||||
// Control-channel requests (permission asks, AskUserQuestion) must be
|
||||
// routed out of the normal event stream and answered (D-78).
|
||||
if (ev['type'] == 'control_request') {
|
||||
@@ -652,6 +674,7 @@ class StreamJsonSession {
|
||||
await _proc.kill();
|
||||
await _items.close();
|
||||
await _statusCtl.close();
|
||||
await _sessionIdCtl.close();
|
||||
await _pendingCtl.close();
|
||||
await _busyCtl.close();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user