Skip to content

Custom agents missing from 'Environment loaded' startup message due to race condition #2081

@silva-gg

Description

@silva-gg

Description

Custom agents defined in .github/agents/ are properly detected and usable via /agent, but they do not appear in the "Environment loaded" startup message. The message shows instructions, skills, and MCP servers, but omits the agent count.

Expected: Environment loaded: 4 custom instructions, 1 MCP server, 58 skills, 38 agents
Actual: Environment loaded: 4 custom instructions, 1 MCP server, 58 skills

Agents are fully functional — /agent lists all 38 custom agents correctly.

Root Cause Analysis

After analyzing app.js (v1.0.5), the startup reporter (Ybt class) fires its onComplete callback exactly once — the first time all registered participants are done. The issue is a registration timing race:

  1. Instructions (VIo), Skills (XIo), and Plugins (QIo) register their reporter participants immediately via useEffect.
  2. Agents (FIo) only registers its participant after the async RAe call completes (when fI.loaded becomes true).
// Agents only register when loaded — too late if others already completed
(0,ke.useEffect)(()=>{
  fI.loaded && FIo(YI, fI.available.length)
}, [YI, fI.available.length, fI.loaded]);

If instructions, skills, and plugins all complete before fI.loaded becomes true, the reporter sees all registered participants as done, fires onComplete, and the startup message is emitted without agents. When agents finally load and FIo registers a new participant, the completion callback has already been emitted (guarded by _completionEmitted flag).

The RAe function includes a remote API call (/agents/swe/custom-agents/{owner}/{repo}) which adds latency, making this race likely for repos with many agents or slower API responses.

Suggested Fix

Option A: Pre-register the agents participant before async loading starts

Register the "agents" participant eagerly so the reporter waits for it:

// Register immediately, report items later when loaded
(0,ke.useEffect)(() => {
  let participant = YI.register("agents");
  // Will be updated when agents load
  return () => {}; // cleanup
}, [YI]);

(0,ke.useEffect)(() => {
  if (fI.loaded) {
    // Reporter already knows about "agents" participant, just update and mark done
    FIo(YI, fI.available.length);
  }
}, [YI, fI.available.length, fI.loaded]);

Option B: Add a timeout/debounce to the reporter completion

Delay the onComplete emission briefly (e.g., 500ms) to allow async participants to register, or use a known list of expected participants.

Environment

  • CLI version: 1.0.5
  • OS: macOS (Darwin arm64)
  • Node.js: v24.11.1
  • Custom agents: 38 (in .github/agents/)
  • Reproduction: Consistent across multiple CLI restarts

Steps to Reproduce

  1. Create a repository with 30+ custom agents in .github/agents/
  2. Launch copilot in the repository
  3. Observe the "Environment loaded" message — agents are missing
  4. Run /agent — all agents are listed correctly

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions