A team launch creates many directories/files in quick succession (worktrees,
inboxes, session logs), and each addDir/unlinkDir event triggered a full
TeamTaskWatchRegistry reconcile that tore down and recreated the entire chokidar
watcher (re-opening a kqueue fd per watched file on macOS). Profiling a 6-member
mixed-team launch showed kqueue churn (kevent) as a top native cost and watcher
rebuild as the top remaining main-thread JS cost after the transcript fix.
Debounce the event-driven reconcile (250ms) so a burst collapses into one rebuild.
collectTargets re-reads the current directory state and emitExistingFilesForNewTargets
backfills files created before the rebuild, so no change is missed; requestReconcile,
startup, and the periodic 30s reconcile stay immediate. Adds a test asserting a
burst of addDir events yields a single rebuild.
During launch, the bootstrap-wait loop polls each member and, per member, re-read
and re-JSON.parsed the same growing transcript tail (readRecentBootstrapTranscriptOutcome
was the top main-thread JS hotspot at ~21% during bootstrap, ~40% with its helpers).
The same file was parsed once per member per poll.
Memoize the parsed tail by (filePath, mtime, size) in a shared cache so the file is
read + parsed once per change and reused across all members. The per-member filter
and failure/success scan is byte-for-byte the same logic; only the redundant read +
JSON.parse is removed. Cache is bounded (LRU, same cap as the outcome cache) and
invalidated on mtime/size change, matching the existing outcome cache semantics.
Adds a test asserting the tail is parsed once and shared while per-member outcome
detection is unchanged.
The main process watched every team directory under ~/.claude/teams (one shallow
chokidar target per team root, per team inboxes, and per task dir). On macOS this
falls back to kqueue, which needs one fd per watched file, so a workspace with
many teams kept ~1600 descriptors open and made startup and reconcile work scale
with the number of teams on disk.
Scope the team-root and task watching to teams that are running or currently
engaged in the UI. The teams root and every team's inboxes are still watched for
all teams, so cross-team message delivery, the lead inbox->stdin relay, and
notifications are unchanged. Idle teams are static, so dropping their team-root/
task watches is safe; opening a team (getData) or launching it re-adds it via an
immediate watch-scope refresh. The provider falls back to watching every team
when unset, and the EMFILE polling fallback is intentionally left unscoped so a
scope change can never look like a deletion.
Measured on a 162-team workspace: open team fds 1600 -> 730, with team-root
watching restored the moment a team is opened or goes live.
listRuntimeProcesses spawned a full `ps -ax` on every call with no caching.
It is invoked very frequently while a team runs: runtime liveness/telemetry
snapshots are rebuilt whenever a team file changes (invalidateRuntimeSnapshotCaches
fires from ~25 sites), so the main process ended up forking ps dozens of times
per second, which is expensive from the large Electron main process and pegged
its CPU.
Add a 1s TTL cache plus in-flight coalescing on the single ps spawn point.
Liveness/telemetry callers already tolerate ~2s staleness via their own snapshot
caches and the OS process table changes negligibly within a second, so this caps
ps to <=1/s without affecting liveness correctness. Measured posix_spawn dropped
from ~146/s to ~11/s with a team running.
The team lead's runtime is tracked via config.leadSessionId, not as a
spawned runtime process. In the OpenCode adapter path,
buildTeamAgentRuntimeSnapshot creates a synthetic runtime entry for the
lead with alive:false (no CLI child pid) and no livenessKind, which the
getCurrentRuntimeOfflineVisualState function then classifies as
stale_runtime. This causes every team to show the lead as stale-runtime
permanently.
Add a guard in getCurrentRuntimeOfflineVisualState to skip the
stale_runtime classification when the member is the team lead and the
runtime entry carries the 'lead' backend marker.
- Extract symlink source/target paths directly from the error message
instead of reconstructing them from process.env (Codex P2 review)
- Add extractSymlinkSourcePath and extractSymlinkTargetPath functions
- Update ensureOpenCodeProfileNodeModulesJunction to accept optional
errorMessage parameter and use extracted paths from it
- Fix unused imports in test (remove 'os', replace 'beforeEach' with
'afterEach' per CodeRabbit review)
- Widen fs.statSync mock signatures to use Parameters<typeof fs.statSync>
per CodeRabbit review
- Add tests for new extraction functions
- Pass errorMessage to ensureOpenCodeProfileNodeModulesJunction calls
in CLI client tests
On Windows 10 without Developer Mode, the OpenCode runtime fails to create
a symlink from shared-cache/config-node_modules to the profile's
node_modules directory. The EPERM error blocks the entire OpenCode provider
catalog, leaving it unavailable.
Changes:
- New openCodeWindowsNodeModulesJunction module that pre-creates a Windows
directory junction (no Developer Mode required) before the runtime call
when an EPERM symlink error is detected
- On Windows, loadView and loadProviderDirectory now detect EPERM symlink
errors, extract the profile ID, create the junction, and retry the
runtime command once before falling back to the error response
- Updated diagnostic hints to accurately reflect that the runtime does not
yet include junction fallback, and that the next runtime update will
include it
- Added unit tests for the junction module and retry behavior
- Extract symlink source/target paths directly from the error message
instead of reconstructing them from process.env (Codex P2 review)
- Add extractSymlinkSourcePath and extractSymlinkTargetPath functions
- Update ensureOpenCodeProfileNodeModulesJunction to accept optional
errorMessage parameter and use extracted paths from it
- Fix unused imports in test (remove 'os', replace 'beforeEach' with
'afterEach' per CodeRabbit review)
- Widen fs.statSync mock signatures to use Parameters<typeof fs.statSync>
per CodeRabbit review
- Add tests for new extraction functions
- Pass errorMessage to ensureOpenCodeProfileNodeModulesJunction calls
in CLI client tests
On Windows 10 without Developer Mode, the OpenCode runtime fails to create
a symlink from shared-cache/config-node_modules to the profile's
node_modules directory. The EPERM error blocks the entire OpenCode provider
catalog, leaving it unavailable.
Changes:
- New openCodeWindowsNodeModulesJunction module that pre-creates a Windows
directory junction (no Developer Mode required) before the runtime call
when an EPERM symlink error is detected
- On Windows, loadView and loadProviderDirectory now detect EPERM symlink
errors, extract the profile ID, create the junction, and retry the
runtime command once before falling back to the error response
- Updated diagnostic hints to accurately reflect that the runtime does not
yet include junction fallback, and that the next runtime update will
include it
- Added unit tests for the junction module and retry behavior
* Add KiloCode as a first-class provider with HTTP-based model catalog
Implements KiloCode (kilo.ai gateway) support following repo design principles,
independently of the OpenCode implementation.
Key changes:
- Add 'kilocode' to CliProviderId, TeamProviderId, MemberWorkSyncProviderId
- Create kilocode-model-catalog feature: HTTP client fetching models from
kilo.ai /models endpoint (not /v1/models — different gateway path)
- Add KILO_API_KEY env var for authentication
- Wire kilocode into provider routing, capabilities, and UI labels
- Add 'kilo' brand icon alias in providerBrandIcons (auto-fetches from models.dev)
- KiloCode status is managed via the HTTP gateway, not the multimodel bridge
* Fix: preserve non-bridge providers (kilocode) when updating provider status
The multimodel bridge only returns status for anthropic/codex/gemini/opencode.
When checkAuthStatus replaced result.providers with the bridge response,
kilocode was lost from the provider list and never appeared in the UI.
Now merge bridge providers with the initial list, keeping any provider
not covered by the bridge so kilocode shows up in the Extensions panel.
* Fix: resolve KiloCode status after bridge merge, skip bridge refresh for non-bridge providers
- resolveKilocodeStatus() gives kilocode a settled verificationState:'verified' status
so isHydratedMultimodelProviderStatus() returns true and the loading spinner stops
- Status reflects KILO_API_KEY presence: authenticated+supported when set, else clear message
- fetchCliStatus() now skips fetchCliProviderStatus for non-bridge providers (kilocode)
so the Claude Code CLI is not queried for kilocode, preventing error status overwrites
* Add KiloCode to API key provider system in settings dialog
isApiKeyProviderId now includes kilocode, so the API key form renders
in the Provider Settings dialog instead of showing an empty modal.
Adds KILO_API_KEY config with placeholder and description.
* Fix KiloCode models endpoint: /api/gateway/models per docs
* Fix: short-circuit getProviderStatus/verifyProviderModels for kilocode
The Claude Code CLI only accepts anthropic and codex for --provider.
Calling it with kilocode caused the blinking modal error.
resolveKilocodeProviderStatus() returns status directly from env
without touching the CLI binary — no bridge, no --provider flag.
* Fix: resolveKilocodeProviderStatus reads from app key store via enrichProviderStatus
process.env.KILO_API_KEY was only set for users who configured it in their
shell environment. The UI stores the key in the app's encrypted key store
(ApiKeyService), which enrichProviderStatus checks via hasStoredProviderApiKey.
Now resolveKilocodeProviderStatus() calls providerConnectionService.enrichProviderStatus()
so both the app key store and env var are checked — the same way anthropic/gemini work.
* Wire KiloCode model catalog into provider status — models now load from gateway
- ProviderConnectionService: add setKilocodeModelCatalogFeature() and
enrichKilocodeProviderStatus() which fetches models from the gateway API
and populates provider.models when the API key is configured
- main/index.ts: create KilocodeModelCatalogFeature at startup and inject
it into ProviderConnectionService, same lifecycle as Codex catalog
* Fix: skip Claude CLI probe for kilocode in prepareForProvisioning
The generic probe path calls probeClaudeRuntime with CLAUDE_CODE_ENTRY_PROVIDER=kilocode
which causes the CLI to hang — freezing the Create Team dialog until timeout.
Add an explicit kilocode case that short-circuits to an API key presence check
(via providerConnectionService.getConnectionInfo) without touching the Claude binary,
same pattern as the opencode adapter bypass.
* Fix vitest localStorage fallback
* test(kilocode): update provider visibility expectations
---------
Co-authored-by: 777genius <quantjumppro@gmail.com>
* feat(i18n): add CJK app locale support
* feat(i18n): add Spanish Hindi and Portuguese locales
* feat(i18n): add French Arabic and Bengali locales
* feat(i18n): add Urdu Indonesian and German locales
* feat(i18n): add landing locales for Bengali Urdu and Indonesian
* fix(i18n): address locale review feedback
---------
Co-authored-by: iliya <iliyazelenkog@gmail.com>
- Add avatar trigger mode to MemberSelect for dense toolbar surfaces.
- Render the lead log source selector beside compact sidebar log search and filters.
- Cover toolbar accessory rendering, avatar trigger behavior and lead alias detection.
- Treat checking, deferred and loading provider model catalog states as pending instead of unavailable.
- Show selected provider activity inside create and launch dialogs while keeping ready providers visible during checks.
- Remove the global provider status header so provider activity is scoped to launch flows.
- Carry bootstrap run ids from bootstrap-state into member evidence and compare them with current run identity.
- Allow small confirmation clock skew for delayed Anthropic app acceptance without accepting stale rapid relaunch evidence.
- Clean confirmed bootstrap members that only have stale persisted runtime pid diagnostics.
- Cover process-table unavailable, post-stop stale pid and mixed launch reconcile cases.
Reset project and repository-group loading, error, and initialized flags when contexts change so stale initialization state cannot suppress fresh context data.
Throttle enhanced splash rendering to 30fps and disable the canvas scene during slow or failed startup steps so the renderer has less work while bootstrapping.
Keep connected provider details visible while refreshes are in flight, restore reusable provider status UI, and separate fast startup summaries from heavier provider hydration. Replace the fixed 30s startup wait with an idle-aware scheduler with a 30s safety cap and cover the Electron timer binding crash.
Share watcher fallback behavior across project, todo, team, and task file monitoring. Add polling fallback coverage for watcher-limit and startup failure cases so Linux EMFILE conditions degrade instead of amplifying renderer crashes.