Skip to main content

Module agent_config

Module agent_config 

Source
Expand description

Pure config-building logic for agent definitions.

Ports the buildConfigFiles, buildMcpConfig, and expandTemplate functions from frontend/app/view/agent/agent-model.ts. Most functions here are pure — no I/O, no async. The exception is the managed skill files manifest section near the end: real filesystem I/O shared by the two independent RPC handlers that materialize config files to disk (agent.open’s write_agent_config_files in server/app_api/agent_open.rs, and writeagentconfig in server/editor_handlers.rs — the latter is the actual “click Launch” path used on every normal agent launch). Lives here rather than in either handler file so the two callers can’t drift out of sync on the manifest format or the path-traversal defense (reagent P1, PR #2322 — writeagentconfig initially had no cleanup at all).

Structs§

AgentConfigFile
A single file to be written to the agent working directory.
ClaudeMdOwnershipMarker 🔒

Constants§

AGENTMUX_MEMORY_FILENAME
AgentMux-owned side file carrying the full Soul+AgentMD+Bundle+Skills composition when the real CLAUDE.md is foreign — always safe to regenerate in place every launch, unlike CLAUDE.md itself in that case.
BUNDLE_SECTION_HEADING
Heading of the bundle-content section in a generated CLAUDE.md.
CLAUDE_MD_IMPORT_MARKER_COMMENT 🔒
Comment wrapping the @import line so its origin — and how to remove it — is unambiguous to anyone reading a foreign CLAUDE.md by hand.
CLAUDE_MD_MANAGED_MARKER
First line AgentMux writes on any CLAUDE.md it fully owns (freshly created, or regenerated on every launch since). Its ABSENCE on an EXISTING file is what marks that file as foreign — predates AgentMux touching this working directory, or a human replaced AgentMux’s file with their own — and is never overwritten again. See docs/specs/SPEC_CLAUDE_MD_OWNERSHIP_PROTECTION_2026_08_22.md.
CLAUDE_MD_OWNERSHIP_MARKER_PATH 🔒
One-time marker recording whether the @import line (below) has already been offered for this working directory. Checked INSTEAD of re-scanning CLAUDE.md content on every launch — without it, a user who deliberately deletes the import line (opting out of AgentMux content entirely) would see it silently reappear on their next launch.
CLAUDE_MD_PRE_ADOPT_BACKUP
Where an adopted legacy CLAUDE.md is kept (is_legacy_agentmux_claude_md).
MANAGED_MCP_SERVERS_MANIFEST
Names of the mcpServers entries AgentMux wrote into a working directory’s .mcp.json on the last materialization — see write_mcp_json_respecting_user_servers. Lives beside MANAGED_SKILL_FILES_MANIFEST for the same reason: it lets the next launch tell entries it owns from entries the user put there.
MANAGED_SKILL_FILES_MANIFEST
Hidden manifest (relative to the agent working directory) tracking which skill-derived paths (.claude/commands/*.md, .claude/skills/*/SKILL.md) AgentMux itself wrote on the last materialization, so a subsequent one can delete ones that are no longer current without touching any user-authored .claude/commands/.claude/skills content.
MCP_JSON_WRITE_LOCK
Held (exclusively) for the whole read-merge-write in write_mcp_json_respecting_user_servers. See its doc comment.
SKILL_DESCRIPTION_MAX_LEN 🔒
Agent Skills spec caps description at 1024 characters.
SKILL_TYPE_AGENT_SKILL
skill_type value that materializes a skill as an Agent Skills-format .claude/skills/<slug>/SKILL.md instead of a .claude/commands/<trigger>.md slash command. Any other value (in practice, always "prompt" today) keeps the pre-existing slash-command behavior. See docs/specs/REPORT_ARMORY_BUNDLE_STANDARD_RESEARCH_2026_07_16.md Phase 0 — skill_type already existed end-to-end but was never branched on before this, making it the lowest-friction place to hang the format discriminator rather than adding a new column.
STARTUP_INSTRUCTIONS_MANAGED_MARKER 🔒
Materialize generated_content (the composed Soul+AgentMD+Bundle+Skills CLAUDE.md body build_config_files already produced) against whatever is already on disk at base_path/CLAUDE.md, WITHOUT ever overwriting a foreign file’s content:

Functions§

agentmux_sessionstart_entry 🔒
The SessionStart hook entry: one agentmux-bashwrap sessionstart --part N command per memory part, N = 1..=HOOK_PARTS (backend::memory_delivery). The commands run in parallel, and each prints its own part (or nothing).
back_up_legacy_claude_md 🔒
Copy a legacy CLAUDE.md to CLAUDE_MD_PRE_ADOPT_BACKUP before it is adopted. Whole or not at all (a failed write removes its partial copy); a copy left by an earlier attempt counts only if it is this file, whole (Codex on #4131).
build_config_files
Build the list of config files to write to the agent working directory.
build_mcp_config
Build .mcp.json content with the auto-injected AgentMux MCP server entry.
build_mcp_config_from_refs
Build .mcp.json from standalone McpServer ref rows (v1 composable model).
build_settings_with_hooks
Build .claude/settings.json content with the auto-injected PreToolUse Bash hook and PreCompact hooks (under the "hooks" key). PreToolUse redirects Bash invocations into the streaming wrapper (agentmux-bashwrap exec); PreCompact (two entries, matcher "manual" / "auto") pings the sidecar the instant compaction starts (agentmux-bashwrap precompact) — see docs/specs/SPEC_COMPACTION_DETECTION_AND_HANDLING_2026_07_31.md §4.2. User-supplied settings.json (from the agent’s content_map["settings"]) is parsed and merged at the top level; user-supplied legacy hooks content (from content_map["hooks"]) is merged into settings.hooks.
cleanup_stale_managed_skill_files
Delete skill-derived files a PREVIOUS materialization wrote (per the on-disk manifest) but that are no longer part of new_managed_paths – e.g. a skill’s format switched between "prompt"/"agent-skill", or it was renamed/removed. Without this, the stale file stays on disk and Claude keeps treating it as active alongside the newly selected format (reagent P1 + Codex P1/P2, PR #2322).
expand_template
Replace {{VARIABLE}} placeholders in content with values from vars.
inject_jekt_signing_keys_into_mcp_json
Best-effort: mint-or-reuse agent_slug’s jekt/LAN signing keys (SPEC_JEKT_TRUST_LAYER_COMPLETION_2026_08_13.md §2.2, SPEC_JEKT_LAN_TIER_SIGNING_2026_08_15.md §2.1) and patch them into a .mcp.json file’s mcpServers.agentmux.env block. Returns None on any parse failure, missing mcpServers.agentmux.env object, or if neither key could be ensured — callers must keep the original content in that case; this must never block writing the agent’s config.
is_agentmux_managed_instructions
Does this file’s content say AgentMux wrote it?
is_generated_skill_row 🔒
A skills-index row exactly as build_config_files renders it: - **<name>**, then optionally (trigger: /<trigger>), then optionally — <description> (Codex on #4131: a bare - **note is a person’s).
is_legacy_agentmux_claude_md
Whether content is exactly what an older AgentMux wrote as CLAUDE.md, in its order, ignoring blank lines: the # Available Skills heading, its usage line, at least one skill row (- **…; AgentMux wrote the section only when there were skills), then optionally the managed import (its comment and line, or the line alone). Anything else means a person wrote there, and it stays theirs.
managed_skill_file_paths
Compute the subset of filenames that are skill-derived managed paths (the ones tracked in MANAGED_SKILL_FILES_MANIFEST) — everything else (CLAUDE.md, .mcp.json, .claude/settings.json, …) is always fully regenerated at a fixed path every launch, so it has no staleness problem to track.
prepend_user_hook_array 🔒
Merge a user-supplied settings.json-level hook-array (PreToolUse or PreCompact) with whatever is already staged in hooks_obj under key (our auto-injected entries, possibly already carrying legacy content_map["hooks"]-merged user entries from the earlier pass in build_settings_with_hooks). User entries are PREPENDED so their matchers/gates get first refusal; AgentMux’s own entries always stay last. A non-array value is warned and dropped rather than silently discarded — same discipline as every other branch in this function (reagent P1 on PR #813).
read_managed_mcp_server_names 🔒
render_skill_md 🔒
Render an Agent Skills-format SKILL.md: YAML frontmatter with the two required fields (name, description — per the spec’s six-field frontmatter; AgentMux doesn’t populate the four optional ones today: license, compatibility, metadata, allowed-tools), followed by the skill’s content as the Markdown body. See https://agentskills.io/specification.
sanitize_trigger 🔒
Validate a skill’s trigger is safe to use as a single path segment in .claude/commands/<trigger>.md. trigger is free-form user input with no format validation anywhere upstream (the skill create/update RPCs and the frontend form all accept it as-is), so a trigger containing a path separator or a .. segment previously let the resulting filename resolve OUTSIDE the agent’s working directory – both for this write and, once stale-file cleanup existed, for the corresponding delete (reagent P1, PR #2322). Rejects (returns None) anything containing / or \, or that is exactly ./..; callers skip writing that skill’s command file entirely rather than silently rewriting the trigger into something the user didn’t ask for.
skill_name_slug 🔒
Derive a slug for an Agent Skill name that is valid per the Agent Skills name grammar: lowercase letters, digits, and hyphens ONLY (no underscores). derive_slug is shared with agent role-slugs, which deliberately DO permit underscores, so it isn’t spec-valid here as-is — hyphenate underscores (and re-collapse any resulting run of hyphens) rather than reusing it directly (Codex P1, PR #2322).
skills_to_agent_skills
Convert standalone Skill records into the AgentSkill shape expected by build_config_files. Used when the v1 ref tables are non-empty.
skills_with_their_own_file 🔒
For each skill, whether build_config_files leaves it a file of its own that Claude Code lists natively: an Agent Skill’s SKILL.md (slugs are made unique), or a command with a usable trigger that no later skill’s command overwrites. Triggers are compared case-insensitively, as a case-insensitive filesystem would. Trigger uniqueness isn’t enforced by the skill store (Codex on #4131). A non-ASCII trigger never counts as owning its file: a normalization-insensitive filesystem (macOS) folds Unicode-equivalent names together, and keeping such a skill in the index costs at most a repeated line, never a lost skill.
truncate_utf16_units 🔒
Truncate s to at most max_units UTF-16 code units, on a UTF-8 char boundary. Mirrors JS .slice(0, n) semantics (JS strings are indexed in UTF-16 code units, so a Rust byte-length cap diverges from the TS mirror for any non-ASCII text — reagent P2, PR #2322).
unique_skill_slug 🔒
Derive a filesystem-safe, COLLISION-FREE, spec-valid slug for an Agent Skill name within one caller-scoped used set. skill_name_slug alone can produce identical output for distinct names that differ only in punctuation/whitespace (e.g. “Deploy Checklist” and “Deploy!!!Checklist” both -> “deploy-checklist”), which would otherwise silently overwrite one skill’s SKILL.md with another’s on disk (reagent P1, PR #2322). Appends -2, -3, … until the slug is unique within used, truncating the base first so the suffixed result never exceeds the spec’s 64-character max (Codex P2, PR #2322 — a 64-char base plus -2 was previously 66 chars).
write_claude_md_respecting_ownership
Shared by agent.open (server/app_api/agent_open.rs) and the WriteAgentConfig “click Launch” path (server/editor_handlers.rs) — per this module’s own doc comment, the two config-materializing call sites that must not drift out of sync. See docs/specs/SPEC_CLAUDE_MD_OWNERSHIP_PROTECTION_2026_08_22.md.
write_managed_mcp_server_names 🔒
Best-effort, like write_managed_skill_file_manifest: losing this write only means the next launch falls back to the no-manifest rule once.
write_managed_skill_file_manifest
Record new_managed_paths as the manifest for the NEXT call to cleanup_stale_managed_skill_files. Best-effort: a write failure is logged, not propagated — losing this write only means the next materialization’s stale-file cleanup is skipped once, not a correctness issue for the current one.
write_mcp_json_respecting_user_servers
Write .mcp.json into base_path without destroying a user’s own MCP server entries (issue #3680). Shared by agent.open and the WriteAgentConfig “click Launch” path — the two config-materializing call sites this module keeps in sync.
write_startup_instructions_respecting_existing
Write a NON-CLAUDE.md startup-instructions file (AGENTS.md, GEMINI.md, QWEN.md, .pi/APPEND_SYSTEM.md, …) WITHOUT ever overwriting a pre-existing, non-AgentMux-authored file at that path.