feat(claude): spawn hosted sessions under the bound account's CLAUDE_CONFIG_DIR (T-484)

The load-bearing piece of the multi-account epic (T-476): a bound workspace's
hosted claude now spawns with CLAUDE_CONFIG_DIR set to that account's dir, so
it runs under the bound account end-to-end. Every hosted session (primary /
secondary / fork / teammate) inherits it — the orchestrator already routes all
spawns through agentBootstrap.

- New pure resolver claudeConfigDirForWorkspace(cwd, boundConfigDir, env):
  bound account dir > parent CLAUDE_CONFIG_DIR (respect the launcher) > null
  (Claude defaults to ~/.claude). The registry is injected as a plain lookup
  so agent_bootstrap stays Flutter-free (its tests run under `dart test`).
- agentBootstrap merges CLAUDE_CONFIG_DIR BEFORE base, so an explicit
  SpawnSpec.env override still wins (override > binding > parent > unset); the
  key is omitted entirely when the resolver returns null.
- Orchestrator carries an optional AccountRegistry; the claude extension builds
  it from ctx.settings. Null in tests → no injection (unchanged behaviour).

No way to SET a binding yet (that's the CLI T-480 / settings UI T-482), so no
changelog entry — the mechanism is in place, the surface lands next. Unit tests
cover the resolver's four states and the envDelta precedence.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-27 17:00:06 +02:00
co-authored by Claude Opus 4.8
parent a8d321f344
commit 1fa7dee863
6 changed files with 110 additions and 6 deletions
+4 -2
View File
@@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:io';
import 'package:clide/clide.dart';
import 'package:clide/builtin/claude/src/account_registry.dart';
import 'package:clide/builtin/claude/src/activity_cluster.dart' show foldLevelFromName, kActivityFoldLevelKey, nextFoldLevel;
import 'package:clide/builtin/claude/src/claude_config.dart';
import 'package:clide/builtin/claude/src/claude_status.dart' show nextSafePermissionMode;
@@ -467,8 +468,9 @@ class ClaudeExtension extends ClideExtension {
}
// The clide-managed session set (T-169). Panes spawn/bind through it so a
// session outlives its pane and is shared across surfaces.
_orchestrator = ClaudeSessionOrchestrator();
// session outlives its pane and is shared across surfaces. The account
// registry (T-476) lets a bound workspace spawn under its own Claude account.
_orchestrator = ClaudeSessionOrchestrator(accountRegistry: AccountRegistry(ctx.settings));
activeSessionOrchestrator = _orchestrator;
// An in-place workspace switch (Open Project/Folder) must not leave the