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/scriptsconfigured 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:
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 tasksFull schema in docs/filesystem-architecture.md §4.
6.4 Service Self-Service (v2.x)
6.4.1 Key-Attached Links
- URL format:
/s/{service_id}?key=sk-svc-xxx - Backend:
consumer_ui.pyinjects template variables; frontend readskeyfromURLSearchParams→ writes tolocalStorage→history.replaceStateimmediately 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_scriptprevents 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: antdTreecheckable +loadDatalazy loading + "All (*)" Switch- Folder selection = entire directory recursively (key ends with
/) - Root constraint:
allowed_docsonly shows/docs,allowed_scriptsonly shows/scripts - Empty
allowed_docsfalls back to["*"], emptyallowed_scriptskeeps empty (semantic = no scripts)
6.5 Consumer Agent Channel-Awareness
create_consumer_agent(..., channel: str = "web")channel="web": does not injectsend_messageeven 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 consumerschannel="wechat": injectssend_message, tool results delivered by the delivery layerchannel="scheduler": same as wechatcache_keyincludes::ch={channel}(only when non-default web)
6.6 Codex / Cursor plan-backed Services (v1.3.1)
Select an authorized connection and model in Settings → Service Management to use the existing web, Consumer API, and Service WeChat entry points. DeepAgents remains the default. The admin's personal WeChat entry point is separate and retains its existing engine path.
Configuration and resources
runtime_choice describes the selection in a create/update request. The server validates and derives runtime_binding; clients cannot supply their own binding. Creation still requires the top-level model. Replace the connection, model, and existing document directory in this minimal example:
{
"name": "Team handbook",
"model": "<authorized-model-id>",
"runtime_choice": {
"runtime": "codex",
"profile_id": "<authorized-connection-id>",
"model": "<authorized-model-id>"
},
"allowed_docs": ["handbook"],
"allowed_scripts": [],
"capabilities": [],
"published": true
}runtime can also be cursor. In allowed_docs and allowed_scripts, "*" grants all resources and an empty array grants none. Set them explicitly instead of relying on defaults. The Service uses its selected System Prompt and User Profile versions; review them before sharing. Admin private long-term memory is not automatically exposed.
Create a Key with {"name":"team","billing":"hosted"}, shown as the authorized-plan option in the UI. Plan Services reject BYOK, including existing BYOK Keys after switching a DeepAgents Service to a plan engine.
Execution path
Service web / Consumer API / Service WeChat
→ Validate Key or WeChat binding, publication state and admin model grant
→ Fixed connection and model → Runtime queue (serial per connection)
→ jellyfish_service_* tools → allowed documents, scripts and current-chat artifacts
→ Streaming events, saved chat, file previews and downloadsapp/runtime/consumer.py handles sessions and execution; consumer_tools.py provides scoped resource tools. Visitors cannot use native command or file approvals or select resource paths belonging to another admin, Service, or conversation.
Changing a connection, model, resource scope, or capability starts a new native session on the next request. Saved Jellyfish history remains. Different Keys / channels do not reuse a native session. Unpublishing, deleting a Key, revoking a grant, or changing permissions cancels the corresponding queued and running tasks.
API and acceptance checks
GET /api/v1/modelsreturnsbilling,default_model,models, andallowed_hosts. Plan Keys use hosted billing and a fixed model. An SDK'smodelfield cannot override that model;provider,api_key,base_url, andregionare rejected.- Native search / image generation must be enabled for the Service and supported by the client, model, and account. Plan Services do not support scheduled tasks, voice, or video; use DeepAgents for those workflows.
- A Service Key is a shared Service-level access boundary, not an individual member identity. Valid Keys can access conversations exposed by that Service. This mode is for trusted internal members.
- Before distribution, verify allowed-document access, rejected out-of-scope paths, continuation, stop, Key revocation, configuration changes, and artifact downloads. Verify real WeChat message and media delivery separately. Unit tests, mocked UI, and builds do not replace real provider and channel checks.
See the Service distribution implementation and acceptance scope.