Appearance
Architecture
How copilot-env's code is arranged: the decisions a reader would otherwise reverse, each as one diagram over real files, then the layer map. Not a user guide (the other pages are), and the checks behind it prove existence only: every path a box names exists, every symbol after a path is exported by that file, and every Demonstrated by: link resolves. No check tests the claim a diagram makes.
Every concept diagram starts at what the flow reads and ends at what it writes or launches. A cylinder is a file or store on this machine, a double-edged box is a network endpoint or a process, and a plain box is code.
Braces mark a value the run fills in: {name} the profile, {host} the Copilot host, {port} the daemon's port, {top} the install root, {tag} and {triple} the release and its build target; the rest ({version}, {uuid}, a spelled-out directory) read as written.
The two wiring modes and the one credential
flowchart TD
credfile[("~/.local/share/copilot-env/state.json (profiles.<name>: the credential slot)")]
prefs[("~/.local/share/copilot-env/state.json (global and profiles.<name>: the settings)")]
store["src/copilot_api/env_state.ts<br>CopilotEnvState StoredCredential ProvisionedCredential"]
cred["src/copilot_api/credential.ts<br>Credential"]
wiring["src/agents/configure.ts<br>resolveCredentialWiring() CredentialWiring ManagedMode ManagedWrite"]
resolver["src/utils/root.ts<br>agentAuthGetArgs() proxyTokenCommand()"]
direct["Direct: the agent config names the Copilot host and a resolver command"]
proxy["proxy: the agent config names the local daemon, which holds the token in memory"]
daemon["src/copilot_api/launch.ts<br>resolveLaunchCredential() DaemonLaunchAuth"]
proxycfg[("~/.local/share/copilot-env/profiles/{name}/config.json<br>the daemon's own auth.apiKeys<br>the default's is ~/.local/share/copilot-env/profiles/default")]
gh[["gh auth token, spawned for a gh-cli slot"]]
claudejson[("~/.claude.json")]
codexw["src/codex/config.ts<br>configureCodexConfig()"]
claudew["src/claude/config.ts<br>configureClaudeConfig()"]
codexfile[("~/.codex/config.toml")]
claudefile[("~/.claude/settings.json")]
daemonenv[["the daemon's env: COPILOT_ENV_DAEMON_GH_TOKEN, COPILOT_ENV_DAEMON_CLIENT_HEADERS, COPILOT_ENV_DAEMON_COPILOT_HOST"]]
credfile -->|"reads the slot: gh-cli, a stored token, or none"| store
prefs -->|"reads static-key"| wiring
proxycfg -->|"static-key on a proxy write: ensureApiKey() reads a key"| wiring
wiring -->|"mints auth.apiKeys when the file holds none"| proxycfg
store --> cred
gh -->|"reads stdout: the OAuth token"| cred
cred -->|"resolve(): the token, or null for a none slot"| wiring
wiring -->|"kind command"| resolver
wiring -->|"mode direct"| direct
wiring -->|"mode proxy"| proxy
direct -->|"base_url https://{host}, http_headers: the Codex user agent, plus Copilot-Integration-Id when one was selected"| codexw
direct -->|"the host, plus env.ANTHROPIC_CUSTOM_HEADERS"| claudew
proxy -->|"base_url http://127.0.0.1:{port}/v1"| codexw
proxy -->|"the loopback base URL"| claudew
codexw -->|"writes model_providers.copilot-env: base_url, then auth.command or http_headers.Authorization"| codexfile
claudew -->|"writes env.ANTHROPIC_BASE_URL, then apiKeyHelper or env.ANTHROPIC_AUTH_TOKEN"| claudefile
claudew -->|"Direct with wire-mcp: registers mcpServers.copilot-env"| claudejson
cred --> daemon
daemon -->|"DaemonCredential: the token rides in env, spliced into argv in-process"| daemonenv- No implicit
ghfallback: anoneslot resolves to null and the caller asks (agent profile auth); a named profile's reason names the profile. static-keyis the one opt-in that bakes the value (authentication: static key owns it):resolveCredentialWiring()resolves it once per agent at the write, and an unresolvable static credential is a failed write, never a silent return to the command shape.
Demonstrated by: test/agents/configure.test.ts, test/commands/auth.e2e.test.ts, test/commands/auth_gh_cli.e2e.test.ts, test/codex/codex_config.e2e.test.ts.
Identity and host: state in the slot, rendered into the agent files
flowchart TD
prefs[("~/.local/share/copilot-env/state.json (global and profiles.<name>: the settings)")]
slotin[("~/.local/share/copilot-env/state.json (profiles.<name>: the credential slot)<br>the profile slot: token, mode, integrationIdentity, copilotHost (read at the start)")]
models[["GET https://{host}/models"]]
user[["GET https://api.github.com/copilot_internal/user"]]
config["src/copilot_api/env_config.ts<br>CopilotEnvConfig"]
state["src/copilot_api/env_state.ts<br>CopilotEnvState StoredDirectPair"]
wire["src/agents/profile_wiring.ts<br>wireBothAgents() DirectResolution resolveDirectWiring()"]
probe["src/codex/config.ts<br>directWiringFor()"]
select["src/copilot_api/integration_identity.ts<br>selectDirectIdentityAndHost() IdentityAndHost probeIntegrationIdentity()"]
pair["src/copilot_api/direct_pair.ts<br>directOverlay() renderDirectPair() landDirectPair()"]
launch["src/copilot_api/launch.ts<br>resolveLaunchCredential()"]
slotout[("~/.local/share/copilot-env/state.json (profiles.<name>: the credential slot)<br>the profile slot, its pair rewritten (written at the end)")]
codexfile[("~/.codex/config.toml<br>base_url https://{host}, http_headers")]
claudefile[("~/.claude/settings-{name}.json<br>env.ANTHROPIC_BASE_URL, env.ANTHROPIC_CUSTOM_HEADERS")]
prefs -->|"reads the identity pin and the host literal"| config
slotin -->|"readProfileDirectPair(): integrationIdentity, copilotHost; a half undefined while never probed"| state
state -->|"stored: pin ?? identity, literal ?? host; no request, no agent file read"| wire
config -->|"the overlay, rendered at read time and never written into the slot"| wire
wire -->|"probe: a credential landing, or a slot holding no pair"| probe
probe --> select
models -->|"first 2xx wins, per identity, on the host in use"| select
user -->|"endpoints.api: the account's designated host"| select
select -->|"identity on the host in use, then the host under it, then re-select where auto moved"| probe
probe -->|"directWiringFor() landing for a profile, commitDefaultWiring() for the default: setProfileDirectPair() writes the halves the probe answered, the pair's only writers"| slotout
wire -->|"both agents, the same pair"| codexfile
wire -->|"both agents, the same pair"| claudefile
state -->|"readProfileDirectPair(): the stored halves"| pair
config -->|"directOverlay(): the pin and the literal"| pair
pair -->|"renderDirectPair(): pin ?? identity, literal ?? host; no request"| launch
pair -->|"landDirectPair(): a half unknown, the same selection at daemon start"| select
pair -->|"setProfileDirectPair(): the probe's own halves, the one copilot_api write"| slotout- The probe memo is process-lifetime and never invalidated (
verdictMemoinintegration_identity.ts, one verdict per token, host, and header set): a CLI run ends in seconds, and the MCP server keeps its verdicts until the transport closes. Injected I/O and a caller deadline bypass it. - The slot is the truth, the agent files are outputs: a re-render (
sync, thecl --profilehook, the Desktop reconcile and its status) renders the slot's pair under the pin and literal in force, never reading a file back. A credential landing (agent profile <name> auth,agent profile <name> add, a settings import) probes and stores. - A re-render makes no request while the pair is stored. A missing pair is landed once through the landing path; the Desktop reconcile alone never lands, it names the repair.
- A credential write takes the previous pair with it, so a definitive refusal leaves the files as they were and an empty pair: a credential refused under every identity works under none, and the next Direct landing probes again.
- A pin is configuration, an overlay: it is rendered over the stored pair and never enters it, so setting or clearing it applies at the next re-render; a landing under a pin or literal stores only the half the probe answered, and the other half is probed once when the overlay is cleared.
Demonstrated by: test/copilot_api/integration_identity.test.ts, test/copilot_api/copilot_host.e2e.test.ts, test/copilot_api/profiles.e2e.test.ts.
Profiles are atomic units
flowchart LR
credfile[("~/.local/share/copilot-env/state.json (profiles.<name>: the credential slot)")]
prefs[("~/.local/share/copilot-env/state.json (global and profiles.<name>: the settings)")]
slot["src/copilot_api/env_state.ts<br>ProfileSlot ProfileMode partialSlotGap() assertProfileSlot()"]
wire["src/agents/profile_wiring.ts<br>bothAgents() wireBothAgents()"]
adapter["src/agents/configure.ts<br>AgentAdapter"]
claude["src/claude/config.ts<br>claudeAdapter() configureClaudeConfig()"]
codex["src/codex/config.ts<br>codexAdapter() configureCodexConfig()"]
claudefile[("~/.claude/settings-{name}.json<br>settings.json for the default")]
desktop[("Claude Desktop's files: {Claude-3p data dir}/configLibrary/{uuid}.json and _meta.json, claude_desktop_config.json, developer_settings.json<br>plus the helper under ~/.local/share/copilot-env/helpers and the claim in state.json (the ownership map)")]
codexfile[("~/.codex/{name}.config.toml and ~/.codex/config.toml<br>config.toml alone for the default")]
credfile -->|"reads profiles.{name}: credential + mode"| slot
prefs -->|"reads claude-desktop"| claude
slot -->|"one credential + one mode"| wire
wire -->|"configureProfile on every adapter, even after one throws"| adapter
adapter --> claude
adapter --> codex
claude -->|"writes env.ANTHROPIC_BASE_URL and the credential carrier"| claudefile
claude -->|"reconciles the entry it owns: reads _meta.json and the ownership map of state.json, then writes the entry, its row, the claim, and the app files"| desktop
codex -->|"writes model_provider, and the model_providers table: copilot-env-{name}, copilot-env for the default"| codexfile- No fallback (authentication: profiles owns the rule):
Credential.resolveWithReason()names the profile in its reason, and a launch on a partial slot reportspartialSlotGap()instead of guessing. - The default is a profile too, under the reserved
defaultkey. Itsmodeis the one mode both agents share.commitDefaultWiring()insrc/agents/configure_defaults.tsrecords it (and the probed pair) after BOTH agents' writes succeeded, so a failed write leaves the previous record;addwith no credential yet records the mode alone. - A single-agent command re-renders the default and never moves its record: with a pair stored it renders that pair; with no record, or a Direct record whose pair a credential write took, it lands both agents as
agent profile addwould. The record has two writers: the landing after both agents wrote, andaddwith no credential yet, which records the mode alone.
Demonstrated by: test/copilot_api/profiles.e2e.test.ts, test/codex/codex_profile_wiring.e2e.test.ts.
The proxy floats; runtime needs are preload shims
flowchart LR
registry[["GET https://registry.npmjs.org/@jeffreycao%2Fcopilot-api"]]
prefs[("~/.local/share/copilot-env/state.json (global and profiles.<name>: the settings)")]
float["src/proxy_float.ts<br>floatProxy() ensureShimDependencies() selectProxyVersion() renderProxyManifest() writeResolvedVersionRecord() readResolvedVersionRecord()"]
shims["src/copilot_api/shims.ts<br>DAEMON_SHIM_DEPENDENCIES shimPath()"]
spawn["src/copilot_api/process.ts<br>DaemonSpec resolveCopilotApiEntry() daemonArgv() daemonEnvironment() launchDaemon()"]
preloads["src/scripts/daemon_lock_preload.ts<br>src/scripts/token_argv_preload.ts<br>src/scripts/daemon_runtime_preload.ts<br>src/scripts/copilot_host_preload.ts<br>src/scripts/client_headers_preload.ts<br>src/scripts/pat_passthrough_preload.ts<br>src/scripts/idle_watchdog_preload.ts<br>src/scripts/log_mute_preload.ts"]
cache[("~/.local/share/copilot-env/bun/cache<br>bun's download cache for the proxy home")]
record[("~/.local/share/copilot-env/proxy/resolved-version.json")]
pcfg[("copilot-env.config, bunfig.toml and package.json, embedded in the binary: PROXY_MIN_VERSION, PROXY_MAX_VERSION, the default cooldown, the shims' dependency ranges")]
phome[("~/.local/share/copilot-env/proxy/install: package.json, bun.lock, bunfig.toml, node_modules<br>the proxy at the resolved version plus the shims' dependencies; the lock pins the transitive tree; [install] minimumReleaseAge is the cooldown; built in install.next, verified, swapped in by rename")]
daemonproc[["the daemon: process.execPath run --no-env-file --no-install --preload {proxy home}/daemon_preload.ts {proxy home}/install/node_modules/@jeffreycao/copilot-api/dist/main.js start, BUN_BE_BUN=1, cwd {proxy home}, NODE_PATH {proxy home}/install/node_modules, COPILOT_API_HOME={the profile's daemon home}"]]
registry -->|"reads versions and publish times"| float
prefs -->|"reads proxy-version, release-cooldown"| float
pcfg -->|"reads the version bounds, the default cooldown, and the dependency ranges"| float
shims -->|"the dependencies every shim imports"| float
float -->|"stages package.json and bunfig.toml, bun install writes the lock and node_modules, the verified tree is swapped in"| phome
float -->|"BUN_INSTALL_CACHE_DIR, one dir per version"| cache
float -->|"writes the version and the time, after the swap; the record follows the tree at every float entry"| record
phome -->|"reads the installed package's version and bin, through the float's own verifier (floatedProxyEntry)"| spawn
shims -->|"the subset DaemonSpec derives"| spawn
spawn -->|"writes daemon_preload.ts in the proxy home, one import per shim in load order, and passes it as the one --preload (daemonPreloadFile, renderDaemonPreload)"| preloads
spawn -->|"launchDaemon(): COPILOT_ENV_ROOT_HOME, COPILOT_ENV_DAEMON_KEEP_PORT"| daemonproc
preloads -->|"run inside it before the package's entry"| daemonproc- The daemon runs from the binary itself:
process.execPathspawned as a bun CLI (BUN_BE_BUN=1in the child's environment; without it a compiled binary would re-run its own entry). A checkout's execPath is bun already. Nothing else is downloaded. - We never patch the package: every shim wraps a runtime seam (
globalThis.fetch,Bun.serve,fs.createWriteStream,process.argv) and touches none of copilot-api's files, so none of them pins the floated version. The /responses WebSocket handshake has no global seam (bun's built-in undici bindsWebSocketnatively), so the client-headers preload re-points the proxy chunks'undiciimport atsrc/scripts/undici_identity.tsthrough a load hook; the files stay untouched. --no-installat spawn gives no second chance, which is why the float installs the shims' dependencies beside the proxy (and a compiled install running aCOPILOT_API_ENTRYfile installs them alone); every spawn carries the token, host, and client-header shims, since a launch without a credential is refused.- The secret-carrying shims stay import-free (
biome/no_shim_imports.grit): a runtime import would drag CLI modules into the daemon process.
Demonstrated by: test/copilot_api/proxy_float.e2e.test.ts, test/copilot_api/daemon_spawn.e2e.test.ts, test/copilot_api/daemon_env_keys.test.ts.
The config store: flag, then stored, then default
flowchart LR
flag[("an explicit flag or env var: agent update --no-verify here, COPILOT_API_VERSION for the proxy pin")]
cmd["src/commands/config.ts<br>runConfig() configTable()"]
prefsin[("~/.local/share/copilot-env/state.json (global and profiles.<name>: the settings)<br>as stored (read at the start)")]
store["src/copilot_api/env_config.ts<br>CopilotEnvConfig<br>src/copilot_api/config_registry.ts<br>CONFIG_REGISTRY ConfigKeyDef"]
dflt["src/copilot_api/config_registry.ts<br>configDefaultValue() configDefaultBoolean() configDefaultNumber()"]
site["src/autoupdate/apply.ts<br>resolveProvenanceDecision()"]
prefsout[("~/.local/share/copilot-env/state.json (global and profiles.<name>: the settings)<br>rewritten whole (written at the end)")]
cmd -->|"--set, --del, --get"| store
prefsin -->|"read()"| store
store -->|"set(), del()"| prefsout
flag -->|"1. wins when given"| site
store -->|"2. the stored value"| site
dflt -->|"3. the built-in default"| site- Every read site applies the precedence itself;
resolveProvenanceDecision()is one of them, taking the flag and the resolved key as two arguments so no stage re-derives the answer. CopilotEnvConfig.read()is strict: an unreadable store throws, because wiring, the proxy pin, and the port knobs must never act on an unproven empty. The three accessors the daemon-side gates reach read degraded instead (autoStartEnabled(),autoUpdateEnabled(),idleTimeoutSeconds()): a throw there would kill the serving daemon, and their flatten is the safe direction (lifecycle off, default window, no self-update).- One registry (
CONFIG_REGISTRY) owns each key's CLI name, storage key, parser, default, and description; docs/configuration.md is pinned against it bytest/copilot_api/docs_config.test.ts.
Demonstrated by: test/copilot_api/env_config.test.ts, test/install/update_apply.e2e.test.ts.
Install, update, provenance
flowchart TD
assets[("the binary's embedded assets<br>materialized: the bin and shell payloads, the daemon shims<br>read in place: copilot-env.config and bunfig.toml, the proxy version bounds and the default cooldown")]
releases[["GET https://api.github.com/repos/Vivswan/copilot-env/releases?per_page=100"]]
download[["https://github.com/Vivswan/copilot-env/releases/download/{tag}: copilot-env-{triple} (.exe on Windows), checksums.txt, attestation.json"]]
tuf[["https://tuf-repo-cdn.sigstore.dev: the Sigstore trust root"]]
prefs[("~/.local/share/copilot-env/state.json (global and profiles.<name>: the settings)")]
autostatein[("{top}/.autoupdate/autoupdate.json<br>as stored (read at the start)")]
root["src/utils/root.ts<br>rootMode() RootMode isProtectedRoot() looksLikeInstallRoot()"]
install["src/install/installer.ts<br>buildInstallPlan() applyInstallPlan() runInstall() pointCurrentAt()"]
update["src/commands/update.ts<br>runUpdate() recheckVerdict()"]
preflight["src/autoupdate/preflight.ts<br>runPreflight()"]
release["src/install/resolve-release.ts<br>resolveTarget() pickAged()"]
apply["src/autoupdate/apply.ts<br>applyUpdate() resolveProvenanceDecision() ProvenanceDecision"]
prov["src/install/provenance.ts<br>verifyReleaseProvenance()"]
att["src/install/attestation.ts<br>RELEASE_SIGNER_POLICY parseStatement() assertSubjectsAttested()"]
layout[("{top}/versions/vX.Y.Z, {top}/current, {top}/bin/agent and agent.ps1")]
rcfile[("~/.bashrc, ~/.zshrc, or the PowerShell $PROFILE")]
autostateout[("{top}/.autoupdate/autoupdate.json<br>rewritten (written at the end)")]
root -->|"checkout or compiled, decided once"| install
root -->|"a checkout refuses without --force"| update
assets -->|"reads at plan time, materializes the payloads into the version root"| install
prefs -->|"reads auto-update, verify-provenance, and update-cooldown as the release age"| preflight
autostatein -->|"reads lastCheckMs: due once a day"| preflight
update --> release
preflight --> release
releases -->|"reads tags and publish dates"| release
release --> apply
download -->|"reads the binary, checksums.txt, attestation.json"| apply
apply -->|"sha256 against checksums.txt, then the attestation"| prov
tuf -->|"reads the trusted root"| prov
prov --> att
apply -->|"stage, provision with the new binary's install --assets-only, commit by flipping current"| install
install -->|"writes versions/vX.Y.Z whole, then flips current"| layout
install -->|"writes the block that sources agents.bashrc or agents.ps1"| rcfile
preflight -->|"writes lastCheckMs, lastResult"| autostateout- Trust on first use: the installer never verifies the release it was fetched from (that would be circular).
agent updateproves origin with Sigstore and fails closed;--no-verifyand theupdate.verify-provenancekey are the two opt-outs, and the skip warning names the way back. - Nothing before the commit is best-effort; past the
currentflip the install has moved forward, soagent migrateand the GC (one previous version kept) may fail without stranding it.
Demonstrated by: test/install/installer.e2e.test.ts, test/install/update_apply.e2e.test.ts, test/install/provenance.test.ts.
The launch path
flowchart LR
rcfile[("~/.bashrc, ~/.zshrc, or the PowerShell $PROFILE")]
prefs[("~/.local/share/copilot-env/state.json (global and profiles.<name>: the settings)")]
env["src/commands/env.ts<br>launcherFunctionLines() runEnv()"]
rc["shell/agents.bashrc<br>shell/agents.ps1"]
bin["bin/agent<br>bin/agent.ps1"]
cli["src/cli.ts"]
launch["src/commands/launch.ts<br>parseLaunchAction() prepareLaunch() writeProfileSettings() runLaunch() LaunchPlan"]
token["src/commands/proxy_token.ts<br>resolveProxyToken() launchProxy()"]
wire["src/agents/profile_wiring.ts<br>wireBothAgents()"]
daemonproc[["agent start, a child, when the proxy is down"]]
configs[("~/.codex/config.toml with ~/.codex/{name}.config.toml, and ~/.claude/settings-{name}.json<br>a default repair rewrites config.toml and settings.json through the agent commands instead")]
catalog[("~/.local/share/copilot-env/codex-model-catalog.json")]
agentcli[["the agent CLI: claude, codex, or copilot, a child with inherited stdio"]]
rcfile -->|"the copilot-env block sources it"| rc
prefs -->|"reads codex-home, codex-host, launchers, and the recorded mode: managedCodexHome() in src/codex/host.ts, managedClaudeBaseUrl() in src/claude/config.ts"| env
env -->|"the only output the agent function evals: CODEX_HOME, ANTHROPIC_BASE_URL, the cl co cx functions"| rc
rc -->|"cl runs agent profile launch claude with the arguments"| bin
bin --> cli
cli --> launch
launch -->|"ensureProxy"| token
launch -->|"syncProfileWiring, on a named Codex launch"| wire
launch -->|"writeClaudeProfileSettings, on a named Claude launch: settings-{name}.json, and the Desktop reconcile the profiles diagram shows"| configs
token -->|"launchProxy(): spawns it"| daemonproc
wire -->|"rewrites both agents' files"| configs
launch -->|"refreshCodexCatalog: a due Codex refresh"| catalog
launch -->|"LaunchPlan: command, args, env, scrub"| agentcli- The managed values are re-read after wiring, never taken from the shell: the wiring step may have moved a port or built the Codex host farm, and nothing refreshes the shell's env between wiring and exec inside one process. The rest of the environment passes through.
- No execve: the agent runs as a child and its exit code (or 128 + signal) passes through; the wiring's write reports are deferred until the agent hands the terminal back.
Demonstrated by: test/commands/launch.e2e.test.ts, test/commands/env.e2e.test.ts, test/shell/shell_integration.e2e.test.ts.
The module map
Each node is one layer, labelled with the paths it owns; an arrow means the layer imports the other. Rendered from architecture.json by bun run docs:arch (--check fails on drift), and the lint in test/utils/architecture.test.ts keeps that declaration equal to the import graph under src/:
| Lint message | What to do |
|---|---|
forbidden import a -> b: src/a/x.ts -> src/b/y.ts; move it or declare the edge | Move the import, or add b under edges.a when the dependency is right |
stale allowance a -> b: no file draws it; remove it from architecture.json | Delete b from edges.a; the declaration lists only edges the code draws |
src/z.ts belongs to no layer in architecture.json | Add the file to a layer or to exclude; nothing is dropped silently |
layer a names src/a/, which owns no file of the graph | Fix the path, or delete it from layers |
An edge is any relative import: runtime, type-only, re-export, side-effect, or a string-literal import(). Imports inside one layer are not edges, and a layer absent from edges imports nothing outside itself.
graph TD cli["src/cli.ts"] commands["src/commands/"] agents["src/agents/"] claude["src/claude/"] codex["src/codex/"] copilot_api["src/copilot_api/"] proxy_float["src/proxy_float.ts"] health["src/health/"] install["src/install/"] autoupdate["src/autoupdate/"] usage["src/usage/"] shell["src/shell/"] mcp["src/mcp/"] migrations["src/migrations/"] scripts["src/scripts/"] utils["src/utils/"] cli --> commands cli --> utils commands --> agents commands --> autoupdate commands --> claude commands --> codex commands --> copilot_api commands --> health commands --> install commands --> mcp commands --> migrations commands --> proxy_float commands --> shell commands --> usage commands --> utils agents --> claude agents --> codex agents --> copilot_api agents --> install agents --> utils claude --> agents claude --> codex claude --> copilot_api claude --> mcp claude --> utils codex --> agents codex --> copilot_api codex --> utils copilot_api --> proxy_float copilot_api --> utils proxy_float --> agents proxy_float --> copilot_api proxy_float --> utils health --> agents health --> autoupdate health --> claude health --> codex health --> copilot_api health --> proxy_float health --> shell health --> utils install --> copilot_api install --> shell install --> utils autoupdate --> copilot_api autoupdate --> install autoupdate --> utils usage --> claude usage --> codex usage --> copilot_api usage --> utils shell --> utils mcp --> copilot_api mcp --> utils migrations --> agents migrations --> autoupdate migrations --> claude migrations --> codex migrations --> copilot_api migrations --> shell migrations --> utils scripts --> copilot_api utils --> copilot_api