{
  "schemaVersion": "1.0",
  "item": {
    "slug": "multi-agent-tenant-upgrade",
    "name": "AI Creation Wizard  + Agent Management Upgrade",
    "source": "tencent",
    "type": "skill",
    "category": "开发工具",
    "sourceUrl": "https://clawhub.ai/maverick-software/multi-agent-tenant-upgrade",
    "canonicalUrl": "https://clawhub.ai/maverick-software/multi-agent-tenant-upgrade",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/multi-agent-tenant-upgrade",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=multi-agent-tenant-upgrade",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "SKILL.md",
      "references/agents-panels-cron.txt",
      "references/agents-utils.txt",
      "references/agents-view.txt",
      "references/app-main.txt",
      "references/app-render-helpers.txt"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
        },
        {
          "label": "Upgrade existing",
          "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "slug": "multi-agent-tenant-upgrade",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-04-29T03:30:59.578Z",
      "expiresAt": "2026-05-06T03:30:59.578Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=multi-agent-tenant-upgrade",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=multi-agent-tenant-upgrade",
        "contentDisposition": "attachment; filename=\"multi-agent-tenant-upgrade-1.5.2.zip\"",
        "redirectLocation": null,
        "bodySnippet": null,
        "slug": "multi-agent-tenant-upgrade"
      },
      "scope": "item",
      "summary": "Item download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this item.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/multi-agent-tenant-upgrade"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/multi-agent-tenant-upgrade",
    "agentPageUrl": "https://openagent3.xyz/skills/multi-agent-tenant-upgrade/agent",
    "manifestUrl": "https://openagent3.xyz/skills/multi-agent-tenant-upgrade/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/multi-agent-tenant-upgrade/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
      },
      {
        "label": "Upgrade existing",
        "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "agent-chat-ux",
        "body": "name: agent-chat-ux\nversion: 1.5.2\nauthor: Charles Sears\ndescription: Multi-agent UX for OpenClaw Control UI — agent selector, per-agent sessions, session history viewer with search, agent-filtered Sessions tab with friendly names, Create Agent wizard, emoji picker, and backend agent CRUD."
      },
      {
        "title": "⚠️ Security & Transparency Notes",
        "body": "Before applying this skill's patches, be aware of the following:"
      },
      {
        "title": "Credential Access (agents.wizard)",
        "body": "The AI Wizard backend (agents.wizard RPC) calls the configured model provider API directly via HTTP. To do this it needs an API key. It resolves credentials in this exact order:\n\nDefault config auth — uses it if the resolved mode is api-key (most common)\nAuth profile store — searches for the first api_key-type profile matching the provider. Reads only provider and type fields to find it; does not log or return values.\nEnvironment variable — ANTHROPIC_API_KEY or OPENAI_API_KEY as a last resort\n\nIf you don't want the wizard reading your auth store, set ANTHROPIC_API_KEY in your environment and ensure your default auth profile is already api-key mode — step 2 is skipped entirely in that case."
      },
      {
        "title": "External API Calls",
        "body": "agents.wizard makes a single HTTP POST to:\n\nhttps://api.anthropic.com/v1/messages (Anthropic models)\nhttps://api.openai.com/v1/chat/completions (OpenAI-compatible models)\n\nNo other outbound calls. The call carries your user-supplied description and nothing else from your system."
      },
      {
        "title": "Patch Scope",
        "body": "These patches modify only agent-related files:\n\nPatchFile modifiedWhat it changesschema-agents.txtsrc/gateway/protocol/schema/agents-models-skills.tsAdds emoji optional param to AgentsUpdateParamsSchemaserver-agents.txtsrc/gateway/server-methods/agents.tsAdds agents.wizard RPC; fixes agents.update to write - Emoji: (not - Avatar:) so emoji edits persist correctlyapp-main.txtui/src/ui/app.tsAdds 19 @state() fields: 10 for Create Agent/Wizard + 9 for edit agent, delete agentapp-render.txtui/src/ui/app-render.tsWires create/wizard props + edit agent save handler (sends emoji param, not avatar; evicts identity cache after save)app-render-helpers.txtui/src/ui/app-render.helpers.tsAgent selector dropdown in chat header (uses resolveAgentEmoji() for correct emoji), per-agent session filter, + New Session buttonagents-view.txtui/src/ui/views/agents.tsCreate Agent panel (manual + wizard modes, 103-emoji picker); Edit agent inline form (name/emoji/workspace); Delete agent with confirmation; always-editable Overviewagents-utils.txtui/src/ui/views/agents-utils.tsbuildModelOptionsMulti() for multi-select fallback dropdownagents-panels-cron.txtui/src/ui/views/agents-panels-status-files.tsCron Jobs tab Scheduler card now shows agent-specific job count and next-wake (not global gateway stats)\n\nEach patch is scoped to a single concern. If any patch file modifies more than the files listed above, stop — you have an outdated copy."
      },
      {
        "title": "LLM Output Validation",
        "body": "Wizard model output is parsed as JSON and validated before use:\n\nMust be a JSON object with name (string), emoji (string), soul (string)\nname is capped at 100 characters, emoji at 10\nsoul must be ≥ 20 characters\nEmpty or non-JSON responses are rejected with a user-visible error — nothing is auto-created"
      },
      {
        "title": "Source Code Modification",
        "body": "This skill applies git apply patches against ~/openclaw and requires a UI + gateway rebuild. Changes are persistent. Always backup before patching:\n\ncd ~/openclaw && git stash  # or git branch backup/pre-agent-ux"
      },
      {
        "title": "1. Agent Selector Dropdown in Chat Header",
        "body": "When multiple agents are configured, a dropdown appears left of the session dropdown in the chat header. Selecting an agent switches to that agent's most recent session (or falls back to a fresh webchat key for that agent). The session dropdown automatically filters to show only sessions belonging to the selected agent."
      },
      {
        "title": "2. Per-Agent Session Filtering (Sorted Newest First)",
        "body": "Sessions are now scoped to the active agent and sorted newest-first. No more mixing other agents' cron jobs and subagent sessions into the current chat's session picker."
      },
      {
        "title": "3. + New Session Button in Chat Header",
        "body": "A + icon button sits right of the session dropdown, allowing new sessions to be started without typing /new."
      },
      {
        "title": "4. Create Agent Panel (Manual + AI Wizard)",
        "body": "The Agents tab gains a + Create Agent button that expands a panel with two modes:\n\nManual mode:\n\nAgent name\nWorkspace path (auto-generated from name if left blank)\nEmoji picker (see below)\n\nAI Wizard mode:\n\nDescribe the agent in plain English\nClick \"Generate Agent\" — AI generates name, emoji, and full SOUL.md\nReview the preview, then click \"✅ Create This Agent\"\n\nAfter creation, the agents list and config form are both refreshed automatically — no \"not found in config\" error, no manual reload needed."
      },
      {
        "title": "5. Emoji Picker Dropdown",
        "body": "The emoji field in Create Agent and Edit Agent forms is a dropdown with 103 curated emojis grouped into 5 categories (Tech & AI, People & Roles, Animals, Nature & Elements, Objects & Symbols), each showing the emoji and its name. A large live preview shows the selected emoji next to the dropdown."
      },
      {
        "title": "6. Edit Agent Inline (Agents Overview)",
        "body": "The Agents Overview card now shows editable inputs directly — no toggle needed:\n\nName, Emoji (dropdown, 103 emojis), Workspace are always editable\nChanges activate the bottom Save button — no separate inline Save/Cancel\nEmoji is saved as - Emoji: in IDENTITY.md (last-wins override of creation value); identity cache is evicted after save so changes appear immediately\nEdit uses the emoji param of agents.update (not avatar) so the correct IDENTITY.md key is written"
      },
      {
        "title": "7. Delete Agent",
        "body": "🗑️ Delete button appears in the Overview header for non-default agents\nInline confirmation dialog before deletion; hidden for the main/default agent"
      },
      {
        "title": "8. Agent-Specific Cron Stats",
        "body": "The Scheduler card on the Cron Jobs tab previously showed global gateway stats (total job count, global next wake). Now:\n\nJobs → count of cron jobs targeting this agent only\nNext wake → earliest nextRunAtMs across this agent's jobs (n/a if no jobs)\nSubtitle → \"Agent cron scheduling status.\" (was \"Gateway cron status.\")\nThis means agents with no crons correctly show Jobs: 0 / Next wake: n/a."
      },
      {
        "title": "9. Agents Tab — Model Selector Cleanup",
        "body": "Removed the redundant read-only \"Primary Model\" row from the Overview grid (it's already editable in the Model Selection section below)\nFallback models converted from a free-text comma-separated input to a proper <select multiple> using the same full model catalog as the primary selector\nAdded spacing and clear labels between primary and fallback fields\nSmall hint \"(hold Ctrl/⌘ to select multiple)\" on the fallback selector"
      },
      {
        "title": "10. Backend — agents.create / agents.update / agents.delete / agents.wizard",
        "body": "New RPC handlers wired into the gateway:\n\nMethodDescriptionagents.createProvisions a new agent entry in config + scaffolds workspace (SOUL.md, AGENTS.md, USER.md)agents.updatePatches agent config (name, workspace, model, identity, etc.)agents.deleteRemoves agent from configagents.wizardCalls the configured LLM to generate name, emoji, and SOUL.md from a plain-text description\n\nAuth fix in agents.wizard: Raw HTTP calls to the model API require an api_key token, not an OAuth/bearer token. The wizard now falls back to an explicit api_key profile (or ANTHROPIC_API_KEY env var) when the default resolved auth mode is oauth or token."
      },
      {
        "title": "11. Session History Viewer (v1.4.0)",
        "body": "A modal overlay accessible from the Sessions tab that displays full conversation history for any session:\n\nAgent dropdown filter — scope sessions by agent\nSession dropdown — pick a session to view (filtered by agent)\nSearch bar — debounced full-text search across message content (case-insensitive)\nRole filter chips — All / User / Assistant / System / Tool\nMessage timeline — role icons (👤/🤖/⚙️/🔧), timestamps, and message text\nPagination — \"Load More\" with count display (100 messages per page)\nClick \"History\" button on any row in the Sessions tab to open"
      },
      {
        "title": "12. Sessions Tab Overhaul (v1.4.0)",
        "body": "The Sessions tab now provides a unified multi-agent experience:\n\nAgent filter dropdown — filter sessions by agent (populated from agents.list)\nFriendly session names — \"Main Session\", \"Cron: pipedream-token-refresh\", \"discord:#bot-chat\" instead of raw keys like agent:main:cron:cc63fdb3-...\nAgent identity column — shows agent emoji + identity name (e.g. \"🤖 Assistant\") using identity.name → name → id fallback chain\nRaw key shown as subtitle — full technical key displayed in smaller muted monospace text below the friendly name\nLabel column removed — redundant since the friendly name already incorporates label/displayName\nCSS grid layout — proper column alignment using grid-template-columns with proportional widths; headers align precisely with data\nEmpty state — clear message when an agent has no sessions\nSession count — total/filtered count shown in the store info line\nHistory button pre-selects — clicking History on a row opens the modal with agent and session already selected, loading history immediately"
      },
      {
        "title": "13. Backend — sessions.history RPC (v1.4.0)",
        "body": "New RPC handler that reads full JSONL transcript files:\n\nParamTypeDescriptionkeystringSession keylimitnumberMax messages (default 200, max 500)offsetnumberPagination offsetsearchstringFull-text search filterrolesFilterstring[]Filter by role(s)\n\nReturns {key, sessionId, agentId, total, offset, items[{role, text, timestamp}]}."
      },
      {
        "title": "14. Auth Mode Badge on Every Assistant Message (v1.5.0 / v1.5.1)",
        "body": "A small pill badge appears on every assistant message group showing which auth method was used:\n\nBadgeColorMeaningOAuthGreenClaude Max OAuth setup token (sk-ant-oat01-*)APIIndigoDirect Anthropic API keyFallbackOrangeOpenAI or other fallback provider\n\nHow it works:\n\nAfter each final chat event, the UI calls auth.status RPC\nThe RPC reads lastGood from auth-profiles.json and returns {profileId, mode}\nchatAuthMode state is updated and passed as fallbackAuthMode to all message groups\nMessages with a specific _authProfileId (from the run pipeline) use that; all others fall back to chatAuthMode\nrenderAuthBadge() handles both profileId strings (e.g. anthropic:manual) and __mode:oauth shorthand\n\nThis ensures every assistant message shows a badge, including tool-use messages and messages from autonomous loops.\n\n\"Up to Date\" pill: The gateway version header now shows \"Up to Date\" in a styled pill matching the Version and Health pills (green dot + rounded backdrop)."
      },
      {
        "title": "15. Pipedream Tab Refreshes on Agent Switch (v1.5.0)",
        "body": "Previously, switching agents while on the Pipedream sub-tab kept showing the previous agent's data. Now onSelectAgent reloads Pipedream (and Zapier) state when their respective sub-tabs are active. Same fix applied to Zapier tab."
      },
      {
        "title": "16. OAuth-First Auth Priority Enforcement (v1.5.1)",
        "body": "The auth profile candidate list is now sorted so token/oauth type profiles always come before api_key profiles, regardless of the order returned by resolveAuthProfileOrder. This guarantees OAuth is tried first for Anthropic even if the profile resolution system silently skips it.\n\n// In pi-embedded-runner/run.ts — applied after resolveAuthProfileOrder()\nconst sortedProfileOrder = [...profileOrder].sort((a, b) => {\n  const typeA = authStore.profiles[a]?.type ?? \"api_key\";\n  const typeB = authStore.profiles[b]?.type ?? \"api_key\";\n  const rank = (t: string) => (t === \"token\" || t === \"oauth\" ? 0 : 1);\n  return rank(typeA) - rank(typeB);\n});"
      },
      {
        "title": "Files Changed",
        "body": "FileChangesrc/gateway/protocol/schema/agents-models-skills.tsAdds emoji optional param to AgentsUpdateParamsSchemasrc/gateway/server-methods/agents.tsagents.wizard RPC; agents.update emoji fix (writes - Emoji: not - Avatar:)ui/src/ui/app-render.helpers.tsAgent dropdown in chat (with resolveAgentEmoji()), per-agent session filter, + New Session buttonui/src/ui/views/agents.tsCreate Agent panel, 103-emoji picker, edit/delete agent UI, always-editable Overviewui/src/ui/views/agents-utils.tsbuildModelOptionsMulti() for multi-select fallback model dropdownui/src/ui/views/agents-panels-status-files.tsCron Jobs tab Scheduler card: agent-specific job count + next wakeui/src/ui/app-render.tsCreate/wizard props wiring + edit agent save handler (emoji param, cache eviction) + session history modal wiring + agent filter for Sessions tab + agent identity name resolution (identity.name fallback chain) + History button agent pre-selectionui/src/ui/app.ts19 @state() fields: create/wizard (10) + edit/delete agent (9) + session history modal (8) + sessions agent filter (1)ui/src/ui/app-view-state.tsSession history modal + sessions agent filter type definitionsui/src/ui/views/sessions.tsOverhauled: friendly names, agent identity column, agent filter dropdown, CSS grid layout, History button, Label column removedui/src/ui/views/sessions-history-modal.tsNew file: Session history modal componentsrc/gateway/protocol/schema/sessions.tsSessionsHistoryParamsSchemasrc/gateway/protocol/schema/types.tsSessionsHistoryParams type exportsrc/gateway/protocol/index.tsvalidateSessionsHistoryParams + re-exportssrc/gateway/server-methods/sessions.tssessions.history RPC handlersrc/agents/pi-embedded-runner/run.tsCalls updateAgentRunContext with authProfileId; sorts profile candidates so token/oauth always firstsrc/gateway/server-chat.tsIncludes authProfileId from run context in final chat event payloadsrc/gateway/server-methods-list.tsRegisters auth.status as a known RPC methodsrc/gateway/server-methods/sessions.tsauth.status RPC: reads lastGood from auth-profiles storesrc/infra/agent-events.tsAdds authProfileId?: string to AgentRunContext; exports updateAgentRunContext()ui/src/styles/chat/grouped.cssAuth badge styles for per-message display (OAuth/API/fallback)ui/src/styles/chat/layout.css.chat-auth-badge styles for chat controls bar badgeui/src/ui/app-gateway.tsCalls auth.status after each final chat event; updates chatAuthMode stateui/src/ui/app-render.helpers.tsAgent dropdown, per-agent session filter, + New Session; auth badge in controls bar removed in v1.5.1 (moved to per-message)ui/src/ui/app-view-state.tsAdds chatAuthMode fieldui/src/ui/app.tsAdds @state() chatAuthModeui/src/ui/chat/grouped-render.tsrenderAuthBadge() for per-message badge; accepts fallbackAuthMode opt; handles __mode:oauth shorthandui/src/ui/controllers/chat.tsAnnotates final messages with _authProfileId from payloadui/src/ui/types/chat-types.tsAdds authProfileId?: string to MessageGroup typeui/src/ui/views/chat.tsgroupMessages() propagates _authProfileId; ChatProps adds chatAuthMode; passes fallbackAuthMode to renderMessageGroupui/src/ui/app-render.tsonSelectAgent reloads Pipedream/Zapier state when sub-tabs active; passes chatAuthMode to chat props; \"Up to Date\" styled as pillui/src/ui/app-chat.tsRemoved CHAT_SESSIONS_ACTIVE_MINUTES time filter (was 120min, now 0 = show all sessions in chat dropdown)"
      },
      {
        "title": "UI Design & Styling Reference",
        "body": "This section documents the UI design decisions for anyone installing or extending this skill."
      },
      {
        "title": "Sessions Tab Layout",
        "body": "Uses CSS grid (display: grid) instead of the default OpenClaw .table flex layout for precise column alignment:\n\n.sessions-grid {\n  grid-template-columns: 2fr 1.2fr 0.6fr 0.8fr 1fr 0.8fr 0.8fr auto;\n  /* Session | Agent | Kind | Updated | Tokens | Thinking | Reasoning | Actions */\n}\n\nHeaders: 12px uppercase, letter-spacing 0.5px, var(--text-muted) color, bottom border\nRows: display: contents for grid participation, subtle bottom border, hover highlight at 2% white opacity\nSession name cell: Friendly name as bold link (var(--accent, #6366f1)), raw key below in 11px muted monospace at 50% opacity\nAgent column: 13px text, emoji + identity name (e.g. \"🤖 Assistant\")\nSelects: max-width: 100px to prevent overflow"
      },
      {
        "title": "Session History Modal",
        "body": "Dark overlay modal with the following structure:\n\n┌──────────────────────────────────────────────────┐\n│  Session History                            [✕]  │\n│  [Agent ▼]  [Session ▼]                         │\n│  [🔍 Search...]  [All] [User] [Asst] [Sys] [Tool]│\n│  ─────────────────────────────────────────────── │\n│  👤 User · Feb 23, 10:23 AM                     │\n│  Hello, can you help me?                         │\n│  ─────────────────────────────────────────────── │\n│  🤖 Assistant · Feb 23, 10:23 AM                │\n│  Of course! What do you need?                    │\n│  ─────────────────────────────────────────────── │\n│          [Load More ↓]  Showing 100 of 342       │\n└──────────────────────────────────────────────────┘\n\nKey CSS variables used:\n\nvar(--bg-card, #1a1a2e) — modal background\nvar(--border, #333) — borders\nvar(--accent, #6366f1) — user role color, active chip\nvar(--text, #e0e0e0) — message text\nvar(--border-subtle, rgba(255,255,255,0.06)) — message separators\n\nRole colors:\n\nRoleColorUservar(--accent, #6366f1) (indigo)Assistant#10b981 (emerald)System#f59e0b (amber)Tool#8b5cf6 (violet)\n\nRole icons: 👤 User, 🤖 Assistant, ⚙️ System, 🔧 Tool, 💬 Other"
      },
      {
        "title": "Session Name Resolution",
        "body": "The friendly name fallback chain:\n\nrow.label (user-set label)\nrow.displayName (server-computed, e.g. \"discord:#bot-chat\")\nSmart key parsing:\n\n*:main → \"Main Session\"\n*:cron:*:run:* → \"Cron Run\"\n*:cron:* → \"Cron Job\"\n*:subagent:* → \"Subagent\"\n*:openai:* → \"OpenAI Session\"\n*:<channel>:direct:<id> → \"Channel · id\"\n*:<channel>:group:* → \"Channel Group\"\n\n\nRaw key as fallback"
      },
      {
        "title": "Agent Identity Resolution",
        "body": "For the Agent column and dropdowns:\n\nagent.identity.name (from IDENTITY.md — e.g. \"Assistant\")\nagent.name (from config — e.g. \"main\")\nagent.id (raw identifier)\n\nEmoji: agent.identity.emoji with \"🤖\" as fallback."
      },
      {
        "title": "Chat Dropdown",
        "body": "The session dropdown in the chat header shows all sessions for the selected agent (no time filter). Previously limited to sessions active within 120 minutes, which hid older Discord channels and other sessions."
      },
      {
        "title": "Installation",
        "body": "This skill requires patching OpenClaw source files and a UI + gateway rebuild."
      },
      {
        "title": "Prerequisites",
        "body": "OpenClaw source at ~/openclaw (fork or local clone)\npnpm installed (npm install -g pnpm)\nNode.js 20+"
      },
      {
        "title": "Step 1: Apply patches",
        "body": "cd ~/openclaw\n\ngit apply ~/.openclaw/workspace/skills/agent-chat-ux/references/schema-agents.txt\ngit apply ~/.openclaw/workspace/skills/agent-chat-ux/references/agents-view.txt\ngit apply ~/.openclaw/workspace/skills/agent-chat-ux/references/agents-utils.txt\ngit apply ~/.openclaw/workspace/skills/agent-chat-ux/references/agents-panels-cron.txt\ngit apply ~/.openclaw/workspace/skills/agent-chat-ux/references/app-render-helpers.txt\ngit apply ~/.openclaw/workspace/skills/agent-chat-ux/references/app-render.txt\ngit apply ~/.openclaw/workspace/skills/agent-chat-ux/references/app-main.txt\ngit apply ~/.openclaw/workspace/skills/agent-chat-ux/references/server-agents.txt\n\nIf any patch fails due to upstream drift, apply manually using the patch file as a line-by-line reference."
      },
      {
        "title": "Step 1b: Apply v1.4.0 patches (Session History + Sessions Tab Overhaul)",
        "body": "cd ~/openclaw\n\n# Backend: sessions.history RPC\ngit apply ~/.openclaw/workspace/skills/agent-chat-ux/references/v1.4.0/patch-01-sessions-history-rpc.txt\n\n# Sessions tab: friendly names, agent column, agent filter\ngit apply ~/.openclaw/workspace/skills/agent-chat-ux/references/v1.4.0/patch-02-sessions-tab-overhaul.txt\n\n# App state + render wiring for history modal\ngit apply ~/.openclaw/workspace/skills/agent-chat-ux/references/v1.4.0/patch-03-app-wiring.txt\n\n# New file: session history modal component\ncp ~/.openclaw/workspace/skills/agent-chat-ux/references/v1.4.0/patch-04-sessions-history-modal.ts \\\n   ui/src/ui/views/sessions-history-modal.ts\n\n# Chat dropdown: show all sessions (remove 120min active filter)\ngit apply ~/.openclaw/workspace/skills/agent-chat-ux/references/v1.4.0/patch-05-chat-sessions-all.txt"
      },
      {
        "title": "Step 1c: Apply v1.5.0 patches (Auth badge + Pipedream agent switch fix)",
        "body": "cd ~/openclaw\n\n# Auth badge (auth.status RPC + per-message badge on all assistant groups)\n# Also includes: OAuth-first sort, \"Up to Date\" pill styling\ngit apply ~/.openclaw/workspace/skills/agent-chat-ux/references/v1.5.0/patch-auth-badge.txt\n\n# Pipedream/Zapier tab refresh on agent switch + chatAuthMode wiring to chat view\ngit apply ~/.openclaw/workspace/skills/agent-chat-ux/references/v1.5.0/patch-pipedream-agent-switch.txt"
      },
      {
        "title": "Step 2: Rebuild UI",
        "body": "cd ~/openclaw\npnpm ui:build"
      },
      {
        "title": "Step 3: Rebuild gateway (for backend agent methods)",
        "body": "cd ~/openclaw\npnpm build"
      },
      {
        "title": "Step 4: Restart gateway",
        "body": "openclaw gateway restart"
      },
      {
        "title": "Step 5: Verify",
        "body": "Open Control UI at http://localhost:18789\nChat tab — agent dropdown appears left of session dropdown (if >1 agent configured); + button appears right of session dropdown\nAgents tab — \"+ Create Agent\" button with Manual and AI Wizard modes\nAgents → Overview → Model Selection — fallback is now a multi-select dropdown\nCreate an agent with the AI Wizard — should generate cleanly and appear in the list with no \"not found\" error\nAgents → Overview — Name, Emoji, Workspace are editable directly; Save button at bottom activates on any change\nChange an agent's emoji — after Save it should persist (not revert to the original creation emoji)\nAgents → Cron Jobs — agents with no cron jobs show Jobs: 0 / Next wake: n/a (not the global gateway count)\nSessions tab — sessions show friendly names (e.g. \"Main Session\") with agent emoji+name column; agent filter dropdown works\nSessions tab → History button — opens modal with conversation history, search, and role filter chips\nHistory modal → Agent filter — changing agent filters the session dropdown; selecting a session loads its messages\nChat dropdown — session dropdown shows ALL sessions for the selected agent (including older Discord channels, OpenAI sessions, etc. — not just recent ones)"
      },
      {
        "title": "Chat: Switching Agents & Sessions",
        "body": "Agent dropdown (left of session): picks the agent; session list updates to show only that agent's sessions\nSession dropdown: switches between existing conversations for the selected agent, newest first\n+ button: starts a new session for the current agent (same as /new)"
      },
      {
        "title": "Agents: Create Agent",
        "body": "Click + Create Agent\nManual: enter name, workspace, pick emoji → \"Create Agent\"\nAI Wizard: describe the agent → \"Generate Agent\" → review preview → \"✅ Create This Agent\""
      },
      {
        "title": "Agents: Fallback Models",
        "body": "In Model Selection:\n\nPrimary model — single dropdown\nFallback models — multi-select (Ctrl/⌘ + click for multiple); these are retried in order when the primary model fails (rate limit, context overflow, etc.)"
      },
      {
        "title": "Sessions: Viewing History",
        "body": "Go to Sessions tab\nOptionally filter by agent using the Agent dropdown\nClick History on any session row\nThe modal opens with the full conversation\nSearch — type to search across all messages (300ms debounce)\nRole chips — click All/User/Assistant/System/Tool to filter by role\nLoad More — pagination loads 100 messages at a time"
      },
      {
        "title": "Sessions: Agent-Filtered View",
        "body": "The Sessions tab now shows:\n\nSession column — friendly name with raw key as subtitle\nAgent column — emoji + name from agent identity\nAgent filter — dropdown at top to scope the view per-agent"
      },
      {
        "title": "Session Key Format",
        "body": "agent:<agentId>:<rest> — the agent selector reads parseAgentSessionKey(state.sessionKey).agentId to determine the active agent and filters the session list accordingly."
      },
      {
        "title": "Config Refresh After Creation",
        "body": "After agents.create succeeds, the UI calls both agents.list (to update the sidebar) and loadConfig (to refresh configForm). Without the loadConfig call, selecting the new agent would show \"not found in config\" because the config form was stale."
      },
      {
        "title": "Wizard Auth Resolution",
        "body": "agents.wizard makes a direct HTTP call to the model provider API. Raw HTTP calls require an api_key type credential — not an OAuth bearer token. The resolution order is:\n\nDefault resolveApiKeyForProvider result (used if mode is api-key)\nFirst api_key-type profile in the auth store for the provider\nANTHROPIC_API_KEY / OPENAI_API_KEY env var directly\n\nThis mirrors the same pattern used in enhanced-loop-hook.ts."
      },
      {
        "title": "Model Fallbacks",
        "body": "Stored as model.fallbacks[] in the agent config. The runtime tries them via runWithModelFallback() when the primary model returns an error."
      },
      {
        "title": "1.5.1 (2026-02-28)",
        "body": "Fix: Auth badge now shows on ALL assistant message groups, including tool-use and autonomous loop messages — chatAuthMode passed as fallbackAuthMode to every message group renderer\nFix: renderAuthBadge() updated to handle __mode:<mode> shorthand in addition to raw profile IDs\nFix: OAuth-first enforcement — profile candidate list is sorted so token/oauth profiles always precede api_key, regardless of resolution order quirks\nFix: Auth badge removed from chat controls bar (was redundant; per-message badge is more informative)\nNew: \"Up to Date\" in gateway version header now renders as a styled pill (green dot + rounded backdrop) matching Version and Health pills\nPatches: Updated patch-auth-badge.txt and patch-pipedream-agent-switch.txt in references/v1.5.0/ to include all v1.5.1 changes"
      },
      {
        "title": "1.5.0 (2026-02-28)",
        "body": "New: Auth mode badge in chat controls bar — shows OAuth / API / Fallback pill after each response via auth.status RPC reading lastGood from auth-profiles store\nNew: auth.status RPC — reads lastGood from loadAuthProfileStore() and returns {profileId, mode} (oauth | api | fallback | unknown)\nNew: chatAuthMode UI state — updates after each final chat event; drives the badge color/label\nFix: Pipedream tab now refreshes when switching agents (was stuck showing previous agent's External User ID)\nFix: Zapier tab also refreshes on agent switch (same fix)\nPatches: 2 patch files in references/v1.5.0/"
      },
      {
        "title": "1.4.0 (2026-02-23)",
        "body": "New: Session History Viewer — modal overlay with full conversation history, full-text search, role filtering (All/User/Assistant/System/Tool), and pagination (100 messages per page)\nNew: sessions.history RPC — reads full JSONL transcripts with search, role filtering, and offset/limit pagination\nNew: Sessions tab agent filter dropdown — scope view to a single agent\nNew: Sessions tab agent identity column — shows emoji + name per row\nOverhaul: Sessions tab now shows friendly display names (\"Main Session\", \"Cron: pipedream-token-refresh\") instead of raw keys (agent:main:main, agent:main:cron:cc63fdb3-...)\nOverhaul: Raw session key shown as muted subtitle under the friendly name for technical reference\nNew: Empty state for agent-filtered sessions — clear message when an agent has no sessions\nNew: Session count shown in store info line\nReplaced: Verbose + Label columns removed from Sessions tab, replaced by Agent column (better multi-agent UX; label was redundant with friendly name)\nDesign: CSS grid layout for Sessions tab — proper column alignment using grid-template-columns with proportional widths\nDesign: Agent identity resolution uses identity.name → config name → agent id fallback chain (shows \"Assistant\" not \"Main Agent\")\nDesign: History button pre-selects agent filter and session in modal, loads history immediately\nDesign: Raw session key shown as muted monospace subtitle for technical reference\nFix: Chat session dropdown now shows ALL sessions (removed CHAT_SESSIONS_ACTIVE_MINUTES 120min time filter that was hiding older Discord channels and API sessions)\nPatches: 5 patch files in references/v1.4.0/"
      },
      {
        "title": "1.3.0 (2026-02-19)",
        "body": "New: Edit agent inline — name, emoji, workspace always editable in Overview; single bottom Save button activates on any change; no inline Save/Cancel toggle\nNew: Delete agent — 🗑️ button with inline confirmation; hidden for default agent\nNew: agents-panels-cron.txt patch — Scheduler card on Cron Jobs tab now shows agent-specific job count and next-wake (n/a when no jobs assigned)\nFix: Emoji reverting after save — agents.update now accepts an emoji param and writes - Emoji: to IDENTITY.md; previously wrote - Avatar: which was always overridden by the creation-time - Emoji: line\nFix: Schema patch added (schema-agents.txt) — AgentsUpdateParamsSchema now includes optional emoji field\nFix: Identity cache eviction after agent save — identity is reloaded immediately so changes are visible without refresh\nFix: Chat dropdown emoji now uses resolveAgentEmoji() to correctly pick up IDENTITY.md emoji (not just agent.identity.emoji)\nExpanded: AGENT_EMOJIS from 60 → 103 entries across all 5 categories"
      },
      {
        "title": "1.2.1 (2026-02-19)",
        "body": "Critical fix: Removed out-of-scope props and handlers from app-render.txt that referenced state not defined by this skill's app-main.txt patch — applying the previous patch would have caused TypeScript errors and runtime crashes\nCritical fix: Removed unused import from app-render.txt\nFix: Replaced remaining as any casts in agent create handlers with typed assertions ({ ok?: boolean; error?: string } | null)"
      },
      {
        "title": "1.2.0 (2026-02-19)",
        "body": "Security: Added Security & Transparency section to SKILL.md documenting credential access, external calls, patch scope, and LLM output validation\nSecurity: .metadata.json now explicitly declares ANTHROPIC_API_KEY/OPENAI_API_KEY as optional env vars with auth resolution order documented\nFix: Stripped out-of-scope state fields from app-main.txt that belonged to an unrelated feature\nHardening: agents.wizard JSON parsing now performs structural validation before accepting model output — rejects non-object, missing fields, empty strings, truncated content\nHardening: name capped to 100 chars, emoji to 10 chars on output to prevent oversized values\nMetadata: Added capabilities block documenting auth_profile_read, external_api_calls, and source_code_patch with mitigations"
      },
      {
        "title": "1.1.0 (2026-02-18)",
        "body": "Fix: AI Wizard 401 error — OAuth token was being passed as x-api-key; now falls back to api_key profile or env var\nFix: \"Agent not found in config\" after creation — loadConfig now called after agents.create in both Manual and Wizard paths\nNew: Emoji picker dropdown (60 emojis, 5 categories, live preview) replaces free-text emoji input\nPatches refreshed with all fixes included"
      },
      {
        "title": "Model Dropdown Shows Only 1–3 Models",
        "body": "This manifests in two distinct failure modes that often alternate. Both are fixed permanently in v1.5.2 source patches. Understanding both helps if you're on an unpatched install.\n\nMode A — Allowlist Trap (agents.defaults.models non-empty)\n\nSymptom: Dropdown shows a handful of models from one provider (e.g. 2 Claude variants) even though many providers have API keys configured. openclaw models list --all shows 750+ models fine.\n\nRoot cause: agents.defaults.models in openclaw.json acts as a strict allowlist inside buildAllowedModelSet() in the models.list RPC handler. Any wizard, openclaw models set <model>, or onboarding command writes a single entry there — after that, only that one provider's model is shown. The key is never auto-cleared.\n\nTemporary fix (unpatched installs):\n\npython3 -c \"\nimport json, re\npath = '/home/charl/.openclaw/openclaw.json'   # adjust path if needed\nraw = open(path).read()\n# strip JS comments, fix trailing commas, then parse\nclean = re.sub(r'//.*\\n', '\\n', raw)\nclean = re.sub(r',\\s*([}\\]])', r'\\1', clean)\ncfg = json.loads(clean)\ncfg.setdefault('agents', {}).setdefault('defaults', {})['models'] = {}\nopen(path, 'w').write(json.dumps(cfg, indent=2))\nprint('done')\n\"\nsystemctl --user restart openclaw-gateway\n\nPermanent fix (source patch — v1.5.2):\nRemove buildAllowedModelSet from the models.list handler entirely. agents.defaults.models controls routing/defaults, not what appears in the UI dropdown. File: src/gateway/server-methods/models.ts.\n\n-      const cfg = loadConfig();\n-      const { allowedCatalog } = buildAllowedModelSet({\n-        cfg,\n-        catalog,\n-        defaultProvider: DEFAULT_PROVIDER,\n-      });\n-      const models = allowedCatalog.length > 0 ? allowedCatalog : catalog;\n+      // Always return the full catalog — agents.defaults.models is for routing,\n+      // not for filtering what's visible in the UI dropdown.\n+      const models = await context.loadGatewayModelCatalog();\n       respond(true, { models }, undefined);\n\nMode B — Empty Catalog on First Gateway Boot\n\nSymptom: Dropdown shows nothing (or only the currently-selected model as a static fallback). Happens right after a fresh systemctl restart. After a second restart it usually works. The models.list RPC takes 1000+ ms and returns { models: [] }.\n\nRoot cause: Three interacting issues:\n\nloadModelCatalog() is lazy — not called until the first models.list RPC. If the gateway was busy at startup (e.g. rebuilding the Control UI, which happens on first install), the dynamic import() of the Pi SDK module may contend with other startup I/O and resolve to an empty registry.\nWhen models.length === 0, modelCatalogPromise is reset to null — so every subsequent call retries, but the error is only logged once (then hasLoggedModelCatalogError suppresses all future failures). This makes the root cause invisible in logs.\nThe loadGatewayModelCatalog() wrapper had no retry — it returned [] as-is.\n\nPermanent fix (source patches — v1.5.2):\n\nsrc/gateway/server-model-catalog.ts — retry once on empty + pre-warm at startup:\n\nexport async function loadGatewayModelCatalog(): Promise<GatewayModelChoice[]> {\n  const result = await loadModelCatalog({ config: loadConfig() });\n  if (result.length === 0) {\n    // Bust cache and retry once — handles transient startup race.\n    return await loadModelCatalog({ config: loadConfig(), useCache: false });\n  }\n  return result;\n}\n\nexport function warmModelCatalogInBackground(): void {\n  loadGatewayModelCatalog().catch(() => {});\n}\n\nsrc/agents/model-catalog.ts — reset error-log gate on each new attempt so failures stay visible:\n\nmodelCatalogPromise = null;\n+      hasLoggedModelCatalogError = false; // allow next attempt to log\n\nsrc/gateway/server.impl.ts — call warmModelCatalogInBackground() at gateway startup (after sidecars):\n\nimport { loadGatewayModelCatalog, warmModelCatalogInBackground } from \"./server-model-catalog.js\";\n// ...inside startGateway(), after startGatewaySidecars():\nwarmModelCatalogInBackground();\n\nDiagnostic Commands\n\n# How many models does the CLI see?\nopenclaw models list --all | wc -l          # should be 750+\n\n# What does the live RPC return? (needs wscat or the gateway WS client)\n# Quickest check — look at the gateway log for models.list response time:\n# < 100ms = hitting cache (good); > 500ms = fresh load (may be empty)\njournalctl --user -u openclaw-gateway --no-pager | grep \"models.list\"\n\n# Is the allowlist populated?\npython3 -c \"\nimport json, re\nraw = open('/home/charl/.openclaw/openclaw.json').read()\nclean = re.sub(r'//.*\\n', '\\n', raw)\nclean = re.sub(r',\\s*([}\\]])', r'\\1', clean)\ncfg = json.loads(clean)\nprint('models allowlist:', cfg.get('agents',{}).get('defaults',{}).get('models',{}))\n\""
      },
      {
        "title": "1.5.2 (2026-03-08)",
        "body": "Permanent model dropdown fix (Mode A + Mode B): Removes buildAllowedModelSet from models.list handler — the dropdown now always shows the full Pi SDK catalog regardless of agents.defaults.models. Adds warmModelCatalogInBackground() called at gateway startup to pre-warm the catalog and prevent the empty-on-first-boot race. Adds retry in loadGatewayModelCatalog() when the first load returns empty. Resets hasLoggedModelCatalogError on each fresh attempt so failures are always logged.\nKnown Gotchas section: Full root-cause writeup for both failure modes with source-level diffs and diagnostic commands."
      },
      {
        "title": "1.0.0 (2026-02-18)",
        "body": "Initial release\nAgent selector dropdown in chat header\nPer-agent session filtering (newest-first)\nNew session button (+) in chat header\nCreate Agent panel — Manual + AI Wizard modes\nFallback model multi-select dropdown\nRemoved duplicate \"Primary Model\" display from Agents overview\nagents.create / agents.update / agents.delete / agents.wizard backend methods"
      }
    ],
    "body": "agent-chat-ux\n\nname: agent-chat-ux\nversion: 1.5.2\nauthor: Charles Sears\ndescription: Multi-agent UX for OpenClaw Control UI — agent selector, per-agent sessions, session history viewer with search, agent-filtered Sessions tab with friendly names, Create Agent wizard, emoji picker, and backend agent CRUD.\n\n⚠️ Security & Transparency Notes\n\nBefore applying this skill's patches, be aware of the following:\n\nCredential Access (agents.wizard)\n\nThe AI Wizard backend (agents.wizard RPC) calls the configured model provider API directly via HTTP. To do this it needs an API key. It resolves credentials in this exact order:\n\nDefault config auth — uses it if the resolved mode is api-key (most common)\nAuth profile store — searches for the first api_key-type profile matching the provider. Reads only provider and type fields to find it; does not log or return values.\nEnvironment variable — ANTHROPIC_API_KEY or OPENAI_API_KEY as a last resort\n\nIf you don't want the wizard reading your auth store, set ANTHROPIC_API_KEY in your environment and ensure your default auth profile is already api-key mode — step 2 is skipped entirely in that case.\n\nExternal API Calls\n\nagents.wizard makes a single HTTP POST to:\n\nhttps://api.anthropic.com/v1/messages (Anthropic models)\nhttps://api.openai.com/v1/chat/completions (OpenAI-compatible models)\n\nNo other outbound calls. The call carries your user-supplied description and nothing else from your system.\n\nPatch Scope\n\nThese patches modify only agent-related files:\n\nPatch\tFile modified\tWhat it changes\nschema-agents.txt\tsrc/gateway/protocol/schema/agents-models-skills.ts\tAdds emoji optional param to AgentsUpdateParamsSchema\nserver-agents.txt\tsrc/gateway/server-methods/agents.ts\tAdds agents.wizard RPC; fixes agents.update to write - Emoji: (not - Avatar:) so emoji edits persist correctly\napp-main.txt\tui/src/ui/app.ts\tAdds 19 @state() fields: 10 for Create Agent/Wizard + 9 for edit agent, delete agent\napp-render.txt\tui/src/ui/app-render.ts\tWires create/wizard props + edit agent save handler (sends emoji param, not avatar; evicts identity cache after save)\napp-render-helpers.txt\tui/src/ui/app-render.helpers.ts\tAgent selector dropdown in chat header (uses resolveAgentEmoji() for correct emoji), per-agent session filter, + New Session button\nagents-view.txt\tui/src/ui/views/agents.ts\tCreate Agent panel (manual + wizard modes, 103-emoji picker); Edit agent inline form (name/emoji/workspace); Delete agent with confirmation; always-editable Overview\nagents-utils.txt\tui/src/ui/views/agents-utils.ts\tbuildModelOptionsMulti() for multi-select fallback dropdown\nagents-panels-cron.txt\tui/src/ui/views/agents-panels-status-files.ts\tCron Jobs tab Scheduler card now shows agent-specific job count and next-wake (not global gateway stats)\n\nEach patch is scoped to a single concern. If any patch file modifies more than the files listed above, stop — you have an outdated copy.\n\nLLM Output Validation\n\nWizard model output is parsed as JSON and validated before use:\n\nMust be a JSON object with name (string), emoji (string), soul (string)\nname is capped at 100 characters, emoji at 10\nsoul must be ≥ 20 characters\nEmpty or non-JSON responses are rejected with a user-visible error — nothing is auto-created\nSource Code Modification\n\nThis skill applies git apply patches against ~/openclaw and requires a UI + gateway rebuild. Changes are persistent. Always backup before patching:\n\ncd ~/openclaw && git stash  # or git branch backup/pre-agent-ux\n\nWhat This Skill Adds\n1. Agent Selector Dropdown in Chat Header\n\nWhen multiple agents are configured, a dropdown appears left of the session dropdown in the chat header. Selecting an agent switches to that agent's most recent session (or falls back to a fresh webchat key for that agent). The session dropdown automatically filters to show only sessions belonging to the selected agent.\n\n2. Per-Agent Session Filtering (Sorted Newest First)\n\nSessions are now scoped to the active agent and sorted newest-first. No more mixing other agents' cron jobs and subagent sessions into the current chat's session picker.\n\n3. + New Session Button in Chat Header\n\nA + icon button sits right of the session dropdown, allowing new sessions to be started without typing /new.\n\n4. Create Agent Panel (Manual + AI Wizard)\n\nThe Agents tab gains a + Create Agent button that expands a panel with two modes:\n\nManual mode:\n\nAgent name\nWorkspace path (auto-generated from name if left blank)\nEmoji picker (see below)\n\nAI Wizard mode:\n\nDescribe the agent in plain English\nClick \"Generate Agent\" — AI generates name, emoji, and full SOUL.md\nReview the preview, then click \"✅ Create This Agent\"\n\nAfter creation, the agents list and config form are both refreshed automatically — no \"not found in config\" error, no manual reload needed.\n\n5. Emoji Picker Dropdown\n\nThe emoji field in Create Agent and Edit Agent forms is a dropdown with 103 curated emojis grouped into 5 categories (Tech & AI, People & Roles, Animals, Nature & Elements, Objects & Symbols), each showing the emoji and its name. A large live preview shows the selected emoji next to the dropdown.\n\n6. Edit Agent Inline (Agents Overview)\n\nThe Agents Overview card now shows editable inputs directly — no toggle needed:\n\nName, Emoji (dropdown, 103 emojis), Workspace are always editable\nChanges activate the bottom Save button — no separate inline Save/Cancel\nEmoji is saved as - Emoji: in IDENTITY.md (last-wins override of creation value); identity cache is evicted after save so changes appear immediately\nEdit uses the emoji param of agents.update (not avatar) so the correct IDENTITY.md key is written\n7. Delete Agent\n🗑️ Delete button appears in the Overview header for non-default agents\nInline confirmation dialog before deletion; hidden for the main/default agent\n8. Agent-Specific Cron Stats\n\nThe Scheduler card on the Cron Jobs tab previously showed global gateway stats (total job count, global next wake). Now:\n\nJobs → count of cron jobs targeting this agent only\nNext wake → earliest nextRunAtMs across this agent's jobs (n/a if no jobs)\nSubtitle → \"Agent cron scheduling status.\" (was \"Gateway cron status.\") This means agents with no crons correctly show Jobs: 0 / Next wake: n/a.\n9. Agents Tab — Model Selector Cleanup\nRemoved the redundant read-only \"Primary Model\" row from the Overview grid (it's already editable in the Model Selection section below)\nFallback models converted from a free-text comma-separated input to a proper <select multiple> using the same full model catalog as the primary selector\nAdded spacing and clear labels between primary and fallback fields\nSmall hint \"(hold Ctrl/⌘ to select multiple)\" on the fallback selector\n10. Backend — agents.create / agents.update / agents.delete / agents.wizard\n\nNew RPC handlers wired into the gateway:\n\nMethod\tDescription\nagents.create\tProvisions a new agent entry in config + scaffolds workspace (SOUL.md, AGENTS.md, USER.md)\nagents.update\tPatches agent config (name, workspace, model, identity, etc.)\nagents.delete\tRemoves agent from config\nagents.wizard\tCalls the configured LLM to generate name, emoji, and SOUL.md from a plain-text description\n\nAuth fix in agents.wizard: Raw HTTP calls to the model API require an api_key token, not an OAuth/bearer token. The wizard now falls back to an explicit api_key profile (or ANTHROPIC_API_KEY env var) when the default resolved auth mode is oauth or token.\n\n11. Session History Viewer (v1.4.0)\n\nA modal overlay accessible from the Sessions tab that displays full conversation history for any session:\n\nAgent dropdown filter — scope sessions by agent\nSession dropdown — pick a session to view (filtered by agent)\nSearch bar — debounced full-text search across message content (case-insensitive)\nRole filter chips — All / User / Assistant / System / Tool\nMessage timeline — role icons (👤/🤖/⚙️/🔧), timestamps, and message text\nPagination — \"Load More\" with count display (100 messages per page)\nClick \"History\" button on any row in the Sessions tab to open\n12. Sessions Tab Overhaul (v1.4.0)\n\nThe Sessions tab now provides a unified multi-agent experience:\n\nAgent filter dropdown — filter sessions by agent (populated from agents.list)\nFriendly session names — \"Main Session\", \"Cron: pipedream-token-refresh\", \"discord:#bot-chat\" instead of raw keys like agent:main:cron:cc63fdb3-...\nAgent identity column — shows agent emoji + identity name (e.g. \"🤖 Assistant\") using identity.name → name → id fallback chain\nRaw key shown as subtitle — full technical key displayed in smaller muted monospace text below the friendly name\nLabel column removed — redundant since the friendly name already incorporates label/displayName\nCSS grid layout — proper column alignment using grid-template-columns with proportional widths; headers align precisely with data\nEmpty state — clear message when an agent has no sessions\nSession count — total/filtered count shown in the store info line\nHistory button pre-selects — clicking History on a row opens the modal with agent and session already selected, loading history immediately\n13. Backend — sessions.history RPC (v1.4.0)\n\nNew RPC handler that reads full JSONL transcript files:\n\nParam\tType\tDescription\nkey\tstring\tSession key\nlimit\tnumber\tMax messages (default 200, max 500)\noffset\tnumber\tPagination offset\nsearch\tstring\tFull-text search filter\nrolesFilter\tstring[]\tFilter by role(s)\n\nReturns {key, sessionId, agentId, total, offset, items[{role, text, timestamp}]}.\n\n14. Auth Mode Badge on Every Assistant Message (v1.5.0 / v1.5.1)\n\nA small pill badge appears on every assistant message group showing which auth method was used:\n\nBadge\tColor\tMeaning\nOAuth\tGreen\tClaude Max OAuth setup token (sk-ant-oat01-*)\nAPI\tIndigo\tDirect Anthropic API key\nFallback\tOrange\tOpenAI or other fallback provider\n\nHow it works:\n\nAfter each final chat event, the UI calls auth.status RPC\nThe RPC reads lastGood from auth-profiles.json and returns {profileId, mode}\nchatAuthMode state is updated and passed as fallbackAuthMode to all message groups\nMessages with a specific _authProfileId (from the run pipeline) use that; all others fall back to chatAuthMode\nrenderAuthBadge() handles both profileId strings (e.g. anthropic:manual) and __mode:oauth shorthand\n\nThis ensures every assistant message shows a badge, including tool-use messages and messages from autonomous loops.\n\n\"Up to Date\" pill: The gateway version header now shows \"Up to Date\" in a styled pill matching the Version and Health pills (green dot + rounded backdrop).\n\n15. Pipedream Tab Refreshes on Agent Switch (v1.5.0)\n\nPreviously, switching agents while on the Pipedream sub-tab kept showing the previous agent's data. Now onSelectAgent reloads Pipedream (and Zapier) state when their respective sub-tabs are active. Same fix applied to Zapier tab.\n\n16. OAuth-First Auth Priority Enforcement (v1.5.1)\n\nThe auth profile candidate list is now sorted so token/oauth type profiles always come before api_key profiles, regardless of the order returned by resolveAuthProfileOrder. This guarantees OAuth is tried first for Anthropic even if the profile resolution system silently skips it.\n\n// In pi-embedded-runner/run.ts — applied after resolveAuthProfileOrder()\nconst sortedProfileOrder = [...profileOrder].sort((a, b) => {\n  const typeA = authStore.profiles[a]?.type ?? \"api_key\";\n  const typeB = authStore.profiles[b]?.type ?? \"api_key\";\n  const rank = (t: string) => (t === \"token\" || t === \"oauth\" ? 0 : 1);\n  return rank(typeA) - rank(typeB);\n});\n\nFiles Changed\nFile\tChange\nsrc/gateway/protocol/schema/agents-models-skills.ts\tAdds emoji optional param to AgentsUpdateParamsSchema\nsrc/gateway/server-methods/agents.ts\tagents.wizard RPC; agents.update emoji fix (writes - Emoji: not - Avatar:)\nui/src/ui/app-render.helpers.ts\tAgent dropdown in chat (with resolveAgentEmoji()), per-agent session filter, + New Session button\nui/src/ui/views/agents.ts\tCreate Agent panel, 103-emoji picker, edit/delete agent UI, always-editable Overview\nui/src/ui/views/agents-utils.ts\tbuildModelOptionsMulti() for multi-select fallback model dropdown\nui/src/ui/views/agents-panels-status-files.ts\tCron Jobs tab Scheduler card: agent-specific job count + next wake\nui/src/ui/app-render.ts\tCreate/wizard props wiring + edit agent save handler (emoji param, cache eviction) + session history modal wiring + agent filter for Sessions tab + agent identity name resolution (identity.name fallback chain) + History button agent pre-selection\nui/src/ui/app.ts\t19 @state() fields: create/wizard (10) + edit/delete agent (9) + session history modal (8) + sessions agent filter (1)\nui/src/ui/app-view-state.ts\tSession history modal + sessions agent filter type definitions\nui/src/ui/views/sessions.ts\tOverhauled: friendly names, agent identity column, agent filter dropdown, CSS grid layout, History button, Label column removed\nui/src/ui/views/sessions-history-modal.ts\tNew file: Session history modal component\nsrc/gateway/protocol/schema/sessions.ts\tSessionsHistoryParamsSchema\nsrc/gateway/protocol/schema/types.ts\tSessionsHistoryParams type export\nsrc/gateway/protocol/index.ts\tvalidateSessionsHistoryParams + re-exports\nsrc/gateway/server-methods/sessions.ts\tsessions.history RPC handler\nsrc/agents/pi-embedded-runner/run.ts\tCalls updateAgentRunContext with authProfileId; sorts profile candidates so token/oauth always first\nsrc/gateway/server-chat.ts\tIncludes authProfileId from run context in final chat event payload\nsrc/gateway/server-methods-list.ts\tRegisters auth.status as a known RPC method\nsrc/gateway/server-methods/sessions.ts\tauth.status RPC: reads lastGood from auth-profiles store\nsrc/infra/agent-events.ts\tAdds authProfileId?: string to AgentRunContext; exports updateAgentRunContext()\nui/src/styles/chat/grouped.css\tAuth badge styles for per-message display (OAuth/API/fallback)\nui/src/styles/chat/layout.css\t.chat-auth-badge styles for chat controls bar badge\nui/src/ui/app-gateway.ts\tCalls auth.status after each final chat event; updates chatAuthMode state\nui/src/ui/app-render.helpers.ts\tAgent dropdown, per-agent session filter, + New Session; auth badge in controls bar removed in v1.5.1 (moved to per-message)\nui/src/ui/app-view-state.ts\tAdds chatAuthMode field\nui/src/ui/app.ts\tAdds @state() chatAuthMode\nui/src/ui/chat/grouped-render.ts\trenderAuthBadge() for per-message badge; accepts fallbackAuthMode opt; handles __mode:oauth shorthand\nui/src/ui/controllers/chat.ts\tAnnotates final messages with _authProfileId from payload\nui/src/ui/types/chat-types.ts\tAdds authProfileId?: string to MessageGroup type\nui/src/ui/views/chat.ts\tgroupMessages() propagates _authProfileId; ChatProps adds chatAuthMode; passes fallbackAuthMode to renderMessageGroup\nui/src/ui/app-render.ts\tonSelectAgent reloads Pipedream/Zapier state when sub-tabs active; passes chatAuthMode to chat props; \"Up to Date\" styled as pill\nui/src/ui/app-chat.ts\tRemoved CHAT_SESSIONS_ACTIVE_MINUTES time filter (was 120min, now 0 = show all sessions in chat dropdown)\nUI Design & Styling Reference\n\nThis section documents the UI design decisions for anyone installing or extending this skill.\n\nSessions Tab Layout\n\nUses CSS grid (display: grid) instead of the default OpenClaw .table flex layout for precise column alignment:\n\n.sessions-grid {\n  grid-template-columns: 2fr 1.2fr 0.6fr 0.8fr 1fr 0.8fr 0.8fr auto;\n  /* Session | Agent | Kind | Updated | Tokens | Thinking | Reasoning | Actions */\n}\n\nHeaders: 12px uppercase, letter-spacing 0.5px, var(--text-muted) color, bottom border\nRows: display: contents for grid participation, subtle bottom border, hover highlight at 2% white opacity\nSession name cell: Friendly name as bold link (var(--accent, #6366f1)), raw key below in 11px muted monospace at 50% opacity\nAgent column: 13px text, emoji + identity name (e.g. \"🤖 Assistant\")\nSelects: max-width: 100px to prevent overflow\nSession History Modal\n\nDark overlay modal with the following structure:\n\n┌──────────────────────────────────────────────────┐\n│  Session History                            [✕]  │\n│  [Agent ▼]  [Session ▼]                         │\n│  [🔍 Search...]  [All] [User] [Asst] [Sys] [Tool]│\n│  ─────────────────────────────────────────────── │\n│  👤 User · Feb 23, 10:23 AM                     │\n│  Hello, can you help me?                         │\n│  ─────────────────────────────────────────────── │\n│  🤖 Assistant · Feb 23, 10:23 AM                │\n│  Of course! What do you need?                    │\n│  ─────────────────────────────────────────────── │\n│          [Load More ↓]  Showing 100 of 342       │\n└──────────────────────────────────────────────────┘\n\n\nKey CSS variables used:\n\nvar(--bg-card, #1a1a2e) — modal background\nvar(--border, #333) — borders\nvar(--accent, #6366f1) — user role color, active chip\nvar(--text, #e0e0e0) — message text\nvar(--border-subtle, rgba(255,255,255,0.06)) — message separators\n\nRole colors:\n\nRole\tColor\nUser\tvar(--accent, #6366f1) (indigo)\nAssistant\t#10b981 (emerald)\nSystem\t#f59e0b (amber)\nTool\t#8b5cf6 (violet)\n\nRole icons: 👤 User, 🤖 Assistant, ⚙️ System, 🔧 Tool, 💬 Other\n\nSession Name Resolution\n\nThe friendly name fallback chain:\n\nrow.label (user-set label)\nrow.displayName (server-computed, e.g. \"discord:#bot-chat\")\nSmart key parsing:\n*:main → \"Main Session\"\n*:cron:*:run:* → \"Cron Run\"\n*:cron:* → \"Cron Job\"\n*:subagent:* → \"Subagent\"\n*:openai:* → \"OpenAI Session\"\n*:<channel>:direct:<id> → \"Channel · id\"\n*:<channel>:group:* → \"Channel Group\"\nRaw key as fallback\nAgent Identity Resolution\n\nFor the Agent column and dropdowns:\n\nagent.identity.name (from IDENTITY.md — e.g. \"Assistant\")\nagent.name (from config — e.g. \"main\")\nagent.id (raw identifier)\n\nEmoji: agent.identity.emoji with \"🤖\" as fallback.\n\nChat Dropdown\n\nThe session dropdown in the chat header shows all sessions for the selected agent (no time filter). Previously limited to sessions active within 120 minutes, which hid older Discord channels and other sessions.\n\nInstallation\n\nThis skill requires patching OpenClaw source files and a UI + gateway rebuild.\n\nPrerequisites\nOpenClaw source at ~/openclaw (fork or local clone)\npnpm installed (npm install -g pnpm)\nNode.js 20+\nStep 1: Apply patches\ncd ~/openclaw\n\ngit apply ~/.openclaw/workspace/skills/agent-chat-ux/references/schema-agents.txt\ngit apply ~/.openclaw/workspace/skills/agent-chat-ux/references/agents-view.txt\ngit apply ~/.openclaw/workspace/skills/agent-chat-ux/references/agents-utils.txt\ngit apply ~/.openclaw/workspace/skills/agent-chat-ux/references/agents-panels-cron.txt\ngit apply ~/.openclaw/workspace/skills/agent-chat-ux/references/app-render-helpers.txt\ngit apply ~/.openclaw/workspace/skills/agent-chat-ux/references/app-render.txt\ngit apply ~/.openclaw/workspace/skills/agent-chat-ux/references/app-main.txt\ngit apply ~/.openclaw/workspace/skills/agent-chat-ux/references/server-agents.txt\n\n\nIf any patch fails due to upstream drift, apply manually using the patch file as a line-by-line reference.\n\nStep 1b: Apply v1.4.0 patches (Session History + Sessions Tab Overhaul)\ncd ~/openclaw\n\n# Backend: sessions.history RPC\ngit apply ~/.openclaw/workspace/skills/agent-chat-ux/references/v1.4.0/patch-01-sessions-history-rpc.txt\n\n# Sessions tab: friendly names, agent column, agent filter\ngit apply ~/.openclaw/workspace/skills/agent-chat-ux/references/v1.4.0/patch-02-sessions-tab-overhaul.txt\n\n# App state + render wiring for history modal\ngit apply ~/.openclaw/workspace/skills/agent-chat-ux/references/v1.4.0/patch-03-app-wiring.txt\n\n# New file: session history modal component\ncp ~/.openclaw/workspace/skills/agent-chat-ux/references/v1.4.0/patch-04-sessions-history-modal.ts \\\n   ui/src/ui/views/sessions-history-modal.ts\n\n# Chat dropdown: show all sessions (remove 120min active filter)\ngit apply ~/.openclaw/workspace/skills/agent-chat-ux/references/v1.4.0/patch-05-chat-sessions-all.txt\n\nStep 1c: Apply v1.5.0 patches (Auth badge + Pipedream agent switch fix)\ncd ~/openclaw\n\n# Auth badge (auth.status RPC + per-message badge on all assistant groups)\n# Also includes: OAuth-first sort, \"Up to Date\" pill styling\ngit apply ~/.openclaw/workspace/skills/agent-chat-ux/references/v1.5.0/patch-auth-badge.txt\n\n# Pipedream/Zapier tab refresh on agent switch + chatAuthMode wiring to chat view\ngit apply ~/.openclaw/workspace/skills/agent-chat-ux/references/v1.5.0/patch-pipedream-agent-switch.txt\n\nStep 2: Rebuild UI\ncd ~/openclaw\npnpm ui:build\n\nStep 3: Rebuild gateway (for backend agent methods)\ncd ~/openclaw\npnpm build\n\nStep 4: Restart gateway\nopenclaw gateway restart\n\nStep 5: Verify\nOpen Control UI at http://localhost:18789\nChat tab — agent dropdown appears left of session dropdown (if >1 agent configured); + button appears right of session dropdown\nAgents tab — \"+ Create Agent\" button with Manual and AI Wizard modes\nAgents → Overview → Model Selection — fallback is now a multi-select dropdown\nCreate an agent with the AI Wizard — should generate cleanly and appear in the list with no \"not found\" error\nAgents → Overview — Name, Emoji, Workspace are editable directly; Save button at bottom activates on any change\nChange an agent's emoji — after Save it should persist (not revert to the original creation emoji)\nAgents → Cron Jobs — agents with no cron jobs show Jobs: 0 / Next wake: n/a (not the global gateway count)\nSessions tab — sessions show friendly names (e.g. \"Main Session\") with agent emoji+name column; agent filter dropdown works\nSessions tab → History button — opens modal with conversation history, search, and role filter chips\nHistory modal → Agent filter — changing agent filters the session dropdown; selecting a session loads its messages\nChat dropdown — session dropdown shows ALL sessions for the selected agent (including older Discord channels, OpenAI sessions, etc. — not just recent ones)\nUsage\nChat: Switching Agents & Sessions\nAgent dropdown (left of session): picks the agent; session list updates to show only that agent's sessions\nSession dropdown: switches between existing conversations for the selected agent, newest first\n+ button: starts a new session for the current agent (same as /new)\nAgents: Create Agent\nClick + Create Agent\nManual: enter name, workspace, pick emoji → \"Create Agent\"\nAI Wizard: describe the agent → \"Generate Agent\" → review preview → \"✅ Create This Agent\"\nAgents: Fallback Models\n\nIn Model Selection:\n\nPrimary model — single dropdown\nFallback models — multi-select (Ctrl/⌘ + click for multiple); these are retried in order when the primary model fails (rate limit, context overflow, etc.)\nSessions: Viewing History\nGo to Sessions tab\nOptionally filter by agent using the Agent dropdown\nClick History on any session row\nThe modal opens with the full conversation\nSearch — type to search across all messages (300ms debounce)\nRole chips — click All/User/Assistant/System/Tool to filter by role\nLoad More — pagination loads 100 messages at a time\nSessions: Agent-Filtered View\n\nThe Sessions tab now shows:\n\nSession column — friendly name with raw key as subtitle\nAgent column — emoji + name from agent identity\nAgent filter — dropdown at top to scope the view per-agent\nArchitecture Notes\nSession Key Format\n\nagent:<agentId>:<rest> — the agent selector reads parseAgentSessionKey(state.sessionKey).agentId to determine the active agent and filters the session list accordingly.\n\nConfig Refresh After Creation\n\nAfter agents.create succeeds, the UI calls both agents.list (to update the sidebar) and loadConfig (to refresh configForm). Without the loadConfig call, selecting the new agent would show \"not found in config\" because the config form was stale.\n\nWizard Auth Resolution\n\nagents.wizard makes a direct HTTP call to the model provider API. Raw HTTP calls require an api_key type credential — not an OAuth bearer token. The resolution order is:\n\nDefault resolveApiKeyForProvider result (used if mode is api-key)\nFirst api_key-type profile in the auth store for the provider\nANTHROPIC_API_KEY / OPENAI_API_KEY env var directly\n\nThis mirrors the same pattern used in enhanced-loop-hook.ts.\n\nModel Fallbacks\n\nStored as model.fallbacks[] in the agent config. The runtime tries them via runWithModelFallback() when the primary model returns an error.\n\nChangelog\n1.5.1 (2026-02-28)\nFix: Auth badge now shows on ALL assistant message groups, including tool-use and autonomous loop messages — chatAuthMode passed as fallbackAuthMode to every message group renderer\nFix: renderAuthBadge() updated to handle __mode:<mode> shorthand in addition to raw profile IDs\nFix: OAuth-first enforcement — profile candidate list is sorted so token/oauth profiles always precede api_key, regardless of resolution order quirks\nFix: Auth badge removed from chat controls bar (was redundant; per-message badge is more informative)\nNew: \"Up to Date\" in gateway version header now renders as a styled pill (green dot + rounded backdrop) matching Version and Health pills\nPatches: Updated patch-auth-badge.txt and patch-pipedream-agent-switch.txt in references/v1.5.0/ to include all v1.5.1 changes\n1.5.0 (2026-02-28)\nNew: Auth mode badge in chat controls bar — shows OAuth / API / Fallback pill after each response via auth.status RPC reading lastGood from auth-profiles store\nNew: auth.status RPC — reads lastGood from loadAuthProfileStore() and returns {profileId, mode} (oauth | api | fallback | unknown)\nNew: chatAuthMode UI state — updates after each final chat event; drives the badge color/label\nFix: Pipedream tab now refreshes when switching agents (was stuck showing previous agent's External User ID)\nFix: Zapier tab also refreshes on agent switch (same fix)\nPatches: 2 patch files in references/v1.5.0/\n1.4.0 (2026-02-23)\nNew: Session History Viewer — modal overlay with full conversation history, full-text search, role filtering (All/User/Assistant/System/Tool), and pagination (100 messages per page)\nNew: sessions.history RPC — reads full JSONL transcripts with search, role filtering, and offset/limit pagination\nNew: Sessions tab agent filter dropdown — scope view to a single agent\nNew: Sessions tab agent identity column — shows emoji + name per row\nOverhaul: Sessions tab now shows friendly display names (\"Main Session\", \"Cron: pipedream-token-refresh\") instead of raw keys (agent:main:main, agent:main:cron:cc63fdb3-...)\nOverhaul: Raw session key shown as muted subtitle under the friendly name for technical reference\nNew: Empty state for agent-filtered sessions — clear message when an agent has no sessions\nNew: Session count shown in store info line\nReplaced: Verbose + Label columns removed from Sessions tab, replaced by Agent column (better multi-agent UX; label was redundant with friendly name)\nDesign: CSS grid layout for Sessions tab — proper column alignment using grid-template-columns with proportional widths\nDesign: Agent identity resolution uses identity.name → config name → agent id fallback chain (shows \"Assistant\" not \"Main Agent\")\nDesign: History button pre-selects agent filter and session in modal, loads history immediately\nDesign: Raw session key shown as muted monospace subtitle for technical reference\nFix: Chat session dropdown now shows ALL sessions (removed CHAT_SESSIONS_ACTIVE_MINUTES 120min time filter that was hiding older Discord channels and API sessions)\nPatches: 5 patch files in references/v1.4.0/\n1.3.0 (2026-02-19)\nNew: Edit agent inline — name, emoji, workspace always editable in Overview; single bottom Save button activates on any change; no inline Save/Cancel toggle\nNew: Delete agent — 🗑️ button with inline confirmation; hidden for default agent\nNew: agents-panels-cron.txt patch — Scheduler card on Cron Jobs tab now shows agent-specific job count and next-wake (n/a when no jobs assigned)\nFix: Emoji reverting after save — agents.update now accepts an emoji param and writes - Emoji: to IDENTITY.md; previously wrote - Avatar: which was always overridden by the creation-time - Emoji: line\nFix: Schema patch added (schema-agents.txt) — AgentsUpdateParamsSchema now includes optional emoji field\nFix: Identity cache eviction after agent save — identity is reloaded immediately so changes are visible without refresh\nFix: Chat dropdown emoji now uses resolveAgentEmoji() to correctly pick up IDENTITY.md emoji (not just agent.identity.emoji)\nExpanded: AGENT_EMOJIS from 60 → 103 entries across all 5 categories\n1.2.1 (2026-02-19)\nCritical fix: Removed out-of-scope props and handlers from app-render.txt that referenced state not defined by this skill's app-main.txt patch — applying the previous patch would have caused TypeScript errors and runtime crashes\nCritical fix: Removed unused import from app-render.txt\nFix: Replaced remaining as any casts in agent create handlers with typed assertions ({ ok?: boolean; error?: string } | null)\n1.2.0 (2026-02-19)\nSecurity: Added Security & Transparency section to SKILL.md documenting credential access, external calls, patch scope, and LLM output validation\nSecurity: .metadata.json now explicitly declares ANTHROPIC_API_KEY/OPENAI_API_KEY as optional env vars with auth resolution order documented\nFix: Stripped out-of-scope state fields from app-main.txt that belonged to an unrelated feature\nHardening: agents.wizard JSON parsing now performs structural validation before accepting model output — rejects non-object, missing fields, empty strings, truncated content\nHardening: name capped to 100 chars, emoji to 10 chars on output to prevent oversized values\nMetadata: Added capabilities block documenting auth_profile_read, external_api_calls, and source_code_patch with mitigations\n1.1.0 (2026-02-18)\nFix: AI Wizard 401 error — OAuth token was being passed as x-api-key; now falls back to api_key profile or env var\nFix: \"Agent not found in config\" after creation — loadConfig now called after agents.create in both Manual and Wizard paths\nNew: Emoji picker dropdown (60 emojis, 5 categories, live preview) replaces free-text emoji input\nPatches refreshed with all fixes included\n⚠️ Known Gotchas\nModel Dropdown Shows Only 1–3 Models\n\nThis manifests in two distinct failure modes that often alternate. Both are fixed permanently in v1.5.2 source patches. Understanding both helps if you're on an unpatched install.\n\nMode A — Allowlist Trap (agents.defaults.models non-empty)\n\nSymptom: Dropdown shows a handful of models from one provider (e.g. 2 Claude variants) even though many providers have API keys configured. openclaw models list --all shows 750+ models fine.\n\nRoot cause: agents.defaults.models in openclaw.json acts as a strict allowlist inside buildAllowedModelSet() in the models.list RPC handler. Any wizard, openclaw models set <model>, or onboarding command writes a single entry there — after that, only that one provider's model is shown. The key is never auto-cleared.\n\nTemporary fix (unpatched installs):\n\npython3 -c \"\nimport json, re\npath = '/home/charl/.openclaw/openclaw.json'   # adjust path if needed\nraw = open(path).read()\n# strip JS comments, fix trailing commas, then parse\nclean = re.sub(r'//.*\\n', '\\n', raw)\nclean = re.sub(r',\\s*([}\\]])', r'\\1', clean)\ncfg = json.loads(clean)\ncfg.setdefault('agents', {}).setdefault('defaults', {})['models'] = {}\nopen(path, 'w').write(json.dumps(cfg, indent=2))\nprint('done')\n\"\nsystemctl --user restart openclaw-gateway\n\n\nPermanent fix (source patch — v1.5.2):\nRemove buildAllowedModelSet from the models.list handler entirely. agents.defaults.models controls routing/defaults, not what appears in the UI dropdown. File: src/gateway/server-methods/models.ts.\n\n-      const cfg = loadConfig();\n-      const { allowedCatalog } = buildAllowedModelSet({\n-        cfg,\n-        catalog,\n-        defaultProvider: DEFAULT_PROVIDER,\n-      });\n-      const models = allowedCatalog.length > 0 ? allowedCatalog : catalog;\n+      // Always return the full catalog — agents.defaults.models is for routing,\n+      // not for filtering what's visible in the UI dropdown.\n+      const models = await context.loadGatewayModelCatalog();\n       respond(true, { models }, undefined);\n\nMode B — Empty Catalog on First Gateway Boot\n\nSymptom: Dropdown shows nothing (or only the currently-selected model as a static fallback). Happens right after a fresh systemctl restart. After a second restart it usually works. The models.list RPC takes 1000+ ms and returns { models: [] }.\n\nRoot cause: Three interacting issues:\n\nloadModelCatalog() is lazy — not called until the first models.list RPC. If the gateway was busy at startup (e.g. rebuilding the Control UI, which happens on first install), the dynamic import() of the Pi SDK module may contend with other startup I/O and resolve to an empty registry.\nWhen models.length === 0, modelCatalogPromise is reset to null — so every subsequent call retries, but the error is only logged once (then hasLoggedModelCatalogError suppresses all future failures). This makes the root cause invisible in logs.\nThe loadGatewayModelCatalog() wrapper had no retry — it returned [] as-is.\n\nPermanent fix (source patches — v1.5.2):\n\nsrc/gateway/server-model-catalog.ts — retry once on empty + pre-warm at startup:\n\nexport async function loadGatewayModelCatalog(): Promise<GatewayModelChoice[]> {\n  const result = await loadModelCatalog({ config: loadConfig() });\n  if (result.length === 0) {\n    // Bust cache and retry once — handles transient startup race.\n    return await loadModelCatalog({ config: loadConfig(), useCache: false });\n  }\n  return result;\n}\n\nexport function warmModelCatalogInBackground(): void {\n  loadGatewayModelCatalog().catch(() => {});\n}\n\n\nsrc/agents/model-catalog.ts — reset error-log gate on each new attempt so failures stay visible:\n\n       modelCatalogPromise = null;\n+      hasLoggedModelCatalogError = false; // allow next attempt to log\n\n\nsrc/gateway/server.impl.ts — call warmModelCatalogInBackground() at gateway startup (after sidecars):\n\nimport { loadGatewayModelCatalog, warmModelCatalogInBackground } from \"./server-model-catalog.js\";\n// ...inside startGateway(), after startGatewaySidecars():\nwarmModelCatalogInBackground();\n\nDiagnostic Commands\n# How many models does the CLI see?\nopenclaw models list --all | wc -l          # should be 750+\n\n# What does the live RPC return? (needs wscat or the gateway WS client)\n# Quickest check — look at the gateway log for models.list response time:\n# < 100ms = hitting cache (good); > 500ms = fresh load (may be empty)\njournalctl --user -u openclaw-gateway --no-pager | grep \"models.list\"\n\n# Is the allowlist populated?\npython3 -c \"\nimport json, re\nraw = open('/home/charl/.openclaw/openclaw.json').read()\nclean = re.sub(r'//.*\\n', '\\n', raw)\nclean = re.sub(r',\\s*([}\\]])', r'\\1', clean)\ncfg = json.loads(clean)\nprint('models allowlist:', cfg.get('agents',{}).get('defaults',{}).get('models',{}))\n\"\n\n1.5.2 (2026-03-08)\nPermanent model dropdown fix (Mode A + Mode B): Removes buildAllowedModelSet from models.list handler — the dropdown now always shows the full Pi SDK catalog regardless of agents.defaults.models. Adds warmModelCatalogInBackground() called at gateway startup to pre-warm the catalog and prevent the empty-on-first-boot race. Adds retry in loadGatewayModelCatalog() when the first load returns empty. Resets hasLoggedModelCatalogError on each fresh attempt so failures are always logged.\nKnown Gotchas section: Full root-cause writeup for both failure modes with source-level diffs and diagnostic commands.\n1.0.0 (2026-02-18)\nInitial release\nAgent selector dropdown in chat header\nPer-agent session filtering (newest-first)\nNew session button (+) in chat header\nCreate Agent panel — Manual + AI Wizard modes\nFallback model multi-select dropdown\nRemoved duplicate \"Primary Model\" display from Agents overview\nagents.create / agents.update / agents.delete / agents.wizard backend methods"
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/maverick-software/multi-agent-tenant-upgrade",
    "publisherUrl": "https://clawhub.ai/maverick-software/multi-agent-tenant-upgrade",
    "owner": "maverick-software",
    "version": "1.5.2",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/multi-agent-tenant-upgrade",
    "downloadUrl": "https://openagent3.xyz/downloads/multi-agent-tenant-upgrade",
    "agentUrl": "https://openagent3.xyz/skills/multi-agent-tenant-upgrade/agent",
    "manifestUrl": "https://openagent3.xyz/skills/multi-agent-tenant-upgrade/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/multi-agent-tenant-upgrade/agent.md"
  }
}