Developer Guide · 06

Service & Consumer Two-Tier Layer

6.1 Admin Layer

  • Login via Web UI with full permissions
  • Manage filesystem (docs/scripts/generated/soul)
  • Configure System Prompt + user profile
  • Manage Subagents + capability prompts
  • Publish Services + manage API Keys
  • Configure scheduled tasks + WeChat integration

6.2 Consumer Layer

  • Authenticate via sk-svc-... API Key (or /s/{sid}?key=... self-service link)
  • Restricted permissions: only access capabilities + allowed_docs/scripts configured in Service
  • Filesystem isolation: users/{admin}/services/{svc}/conversations/{conv}/
  • Generated file isolation: each conversation has its own generated/
  • Cannot access Admin's filesystem

6.3 Service Configuration

Each Service is a published config:

text
users/{admin_id}/services/{service_id}/
├── config.json          # Model + system_prompt + capabilities + allowed_docs/scripts + welcome_message + quick_questions + wechat_channel
├── keys.json            # API Keys (hashed)
├── wechat_sessions.json # WeChat session state
├── conversations/       # Consumer conversation directory
└── tasks/               # Service scheduled tasks

Full schema in docs/filesystem-architecture.md §4.

6.4 Service Self-Service (v2.x)

  • URL format: /s/{service_id}?key=sk-svc-xxx
  • Backend: consumer_ui.py injects template variables; frontend reads key from URLSearchParams → writes to localStoragehistory.replaceState immediately clears query
  • Frontend admin: Key Modal on success additionally shows full link with key + warning (equivalent to sharing the key)

6.4.2 Welcome Message + Quick Questions

  • Fields: welcome_message: str + quick_questions: List[str]
  • Backend template injection: _safe_json_for_inline_script prevents script breakout
  • Frontend chat: ChatGPT-style first screen (large welcome text + gradient chips), auto-hides after first message

6.4.3 Visual File/Script Selector

  • FileTreePicker.tsx: antd Tree checkable + loadData lazy loading + "All (*)" Switch
  • Folder selection = entire directory recursively (key ends with /)
  • Root constraint: allowed_docs only shows /docs, allowed_scripts only shows /scripts
  • Empty allowed_docs falls back to ["*"], empty allowed_scripts keeps empty (semantic = no scripts)

6.5 Consumer Agent Channel-Awareness

python
create_consumer_agent(..., channel: str = "web")
  • channel="web": does not inject send_message even if humanchat capability is enabled. Reason: agent output on web is already streamed to browser; calling send_message would have no delivery target and would expose tool events to consumers
  • channel="wechat": injects send_message, tool results delivered by the delivery layer
  • channel="scheduler": same as wechat
  • cache_key includes ::ch={channel} (only when non-default web)