Studio Agent Bridge
Use this skill when the user wants OpenClaw to talk to a remote Studio Agent through a local proxy process.
Typical triggers:
- "帮我创建个 lakehouse 任务"
- "用 Studio Agent 帮我 ..."
- "在 ClickZetta 里执行/创建 ..."
Install then Configure (Required)
After installing this skill (local folder or ClawHub), run this once before first use:
cp skills/studio-agent/studio-agent.config.example.json studio-agent.config.json
Edit studio-agent.config.json with real values:
wsUrl(base host only, for examplelocalhost:8000ordev-api.clickzetta.com)tokeninstanceIdinstanceNameprojectIdworkspace
Validate (optional):
node skills/studio-agent/scripts/configure-skill.mjs validate --input studio-agent.config.json
Apply (required):
node skills/studio-agent/scripts/configure-skill.mjs apply --input studio-agent.config.json --replace --restart
Runtime note: if this setup step is skipped, this skill must return the same command block to the user instead of only listing missing CZ_* env vars.
What this skill provides
- A local proxy script:
scripts/cz-agent-proxy.mjs - A one-shot runner script:
scripts/cz-agent-oneshot.mjs(recommended default) - Protocol bridge:
- stdin JSON line -> Studio
InboundMessage - Studio
OutboundMessage-> stdout JSON line (assistant_delta/assistant_final/error)
- stdin JSON line -> Studio
- Behavior:
- startup-connect check
- auto create-conversation bootstrap when
conversation_idis omitted - serial request guard (no concurrent
user_input) - stop-grace window
- request timeout
- reconnect attempts
Execution policy
- Do not ask the user for
CZ_*env vars up front. - Assume env is already wired via OpenClaw config (
skills.entries["studio-agent"].env) unless runtime errors prove otherwise. - When runtime reports missing/invalid Studio config, return a copy-paste setup block:
cp skills/studio-agent/studio-agent.config.example.json studio-agent.config.jsonnode skills/studio-agent/scripts/configure-skill.mjs validate --input studio-agent.config.jsonnode skills/studio-agent/scripts/configure-skill.mjs apply --input studio-agent.config.json --replace --restart
- Default path for normal user requests: run
scripts/cz-agent-oneshot.mjsonce and return its result. - Only use raw
scripts/cz-agent-proxy.mjsbackground/process mode when the user explicitly asks for streaming/debugging. - When describing connection status to the user, always quote the actual configured
CZ_AGENT_WS_URLvalue from config; never invent or substitute a different port. - Do not rely on implicit tool-runtime env inheritance for this skill.
- Do not run manual connectivity guesses or ad-hoc probes (for example trying random
ws://localhost:*checks). The only connection source of truth isscripts/cz-agent-proxy.mjs. - Never use shell
timeoutcommand in this skill flow (macOS may not have it). - In normal flow, do not enter
process action:list/poll/writeloops. - For requests like "帮我创建个 lakehouse 任务", immediately:
- run one-shot runner command
- read runner JSON output
- if
ok=true, returncontent; ifok=false, return concise error and stop
- If runner returns
ok=true, treat Studio as connected and continue the user workflow; do not claim Studio is down. - Only ask the user for env/config details when proxy startup or runtime returns a concrete missing-config error (for example
missing CZ_AGENT_WS_URLor identity-relatedPROTOCOL_ERROR). - If remote asks follow-up fields (task type, SQL details, etc.), relay those follow-up questions to the user as normal.
- For
user_input, the proxy auto-normalizes metadata to frontend-compatible shape:- default
metadata.source = "openclaw"when missing - default
metadata.configs = [{"type":"text","value":"<user_input>"}]when missing
- default
- For
interrupt_request, proxy auto-sendsinterrupt_decisionby default (CZ_INTERRUPT_DECISION_MODE=auto_approve) so requests do not hang waiting for manual confirmation. - Proxy defaults to compact output for OpenClaw context safety:
CZ_EMIT_ASSISTANT_DELTAS=falseby default (onlyassistant_final/errorare emitted)- when deltas are enabled,
contentis omitted from delta events to avoid repeated cumulative payloads
Mandatory one-shot command for normal requests:
node {baseDir}/scripts/cz-agent-oneshot.mjs --input "<user_input>"
Runner output is one JSON object:
- success:
{"ok":true,"content":"...","conversation_id":"...","request_id":"..."} - failure:
{"ok":false,"error":{"code":"...","message":"..."}, ...}
Advanced proxy start template (debug/multi-turn only; fill values from skills.entries["studio-agent"].env):
cd {baseDir} && CZ_AGENT_WS_URL="..." CZ_USER_ID="..." CZ_TENANT_ID="..." CZ_INSTANCE_ID="..." CZ_SESSION_ID="..." CZ_AUTO_CREATE_CONVERSATION="..." CZ_CONVERSATION_TITLE="..." node scripts/cz-agent-proxy.mjs
Required env vars
Set these in the runtime where the proxy process runs:
CZ_AGENT_WS_URL(required): Studio WebSocket URL, e.g. localws://127.0.0.1:8000/wsor remotewss://dev-api.clickzetta.com/ai?...CZ_AGENT_TOKEN(optional): auth token (normally carried inCZ_AGENT_WS_URLquery param)CZ_USER_ID(optional): auto-derived from token when absentCZ_TENANT_ID(optional): auto-derived from token when absentCZ_INSTANCE_ID(required unless passed in each stdinidentity)CZ_SESSION_ID(optional, defaultopenclaw-session)CZ_CONVERSATION_ID(optional): default conversation id used when stdin line omitsconversation_idCZ_AUTO_CREATE_CONVERSATION(optional, defaulttrue): when noconversation_idis available, create a fresh conversation at startupCZ_CONVERSATION_TITLE(optional, defaultOpenClaw Session): title for auto-created conversations
Optional tuning:
CZ_REQUEST_TIMEOUT_SECONDS(default120)CZ_STOP_GRACE_SECONDS(default10)CZ_STARTUP_CONNECT_TIMEOUT_SECONDS(default10)CZ_RECONNECT_MAX_ATTEMPTS(default3)CZ_ALWAYS_ALLOW_TOOLS(optional): comma-separated tool names injected asmetadata.always_allow_toolswhen request metadata omits itCZ_INTERRUPT_DECISION_MODE(optional):auto_approve(default) |auto_reject|offCZ_EMIT_ASSISTANT_DELTAS(optional, defaultfalse): emitassistant_deltaevents when true
Optional identity enrichments (recommended for task/query permissions in some Studio deployments):
CZ_INSTANCE_NAMECZ_PROJECT_IDCZ_PROJECT_NAMECZ_WORKSPACECZ_WORKSPACE_IDCZ_USERNAME
Self-Managed Config
This skill includes a config manager script so users can configure all required values in one place.
Minimal required input fields for SaaS users:
wsUrl(base only, for examplelocalhost:8000)tokeninstanceIdinstanceNameprojectIdworkspace
The script builds final CZ_AGENT_WS_URL automatically as:
- when path missing: local host ->
<wsUrl>/ws?..., remote host -><wsUrl>/ai?... - when full URL path is provided (for example
/ai), keep the path unchanged - always normalize query to include
x-clickzetta-token=<token>&env=prod
Template:
node skills/studio-agent/scripts/configure-skill.mjs template > studio-agent.config.json
Validate:
node skills/studio-agent/scripts/configure-skill.mjs validate --input studio-agent.config.json
Apply to OpenClaw config and restart gateway:
node skills/studio-agent/scripts/configure-skill.mjs apply --input studio-agent.config.json --replace --restart
Notes:
- Config is written only to
skills.entries["studio-agent"].env. - Token is normalized into
CZ_AGENT_WS_URLquery paramx-clickzetta-token. envis fixed toprodin built websocket URL.--replacekeeps config minimal and removes stale extra keys from previous setups.- By default,
CZ_AGENT_TOKENis not persisted (OpenClaw may block token env overrides for skills).
Advanced: Start the proxy
Run in background only when you need manual streaming/debug behavior:
bash pty:true background:true command:"node {baseDir}/scripts/cz-agent-proxy.mjs"
Use process action:list to find the session id.
If conversation_id is omitted, the proxy will use CZ_CONVERSATION_ID or an auto-created conversation.
Send one user turn
Send exactly one JSON object per line:
{
"version": "v1",
"op": "user_input",
"request_id": "req-1",
"identity": {
"user_id": "u-1",
"tenant_id": "t-1",
"instance_id": "inst-1",
"session_id": "sess-1"
},
"user_input": "Explain this function",
"metadata": {
"source": "openclaw",
"configs": [{ "type": "text", "value": "Explain this function" }]
}
}
Send with process action:write and append \n:
process action:write sessionId:<proxy-session-id> data:'{"version":"v1","op":"user_input","request_id":"req-1","identity":{"user_id":"u-1","tenant_id":"t-1","instance_id":"inst-1","session_id":"sess-1"},"user_input":"Explain this function","metadata":{"source":"openclaw","configs":[{"type":"text","value":"Explain this function"}]}}\n'
Stop current generation
process action:write sessionId:<proxy-session-id> data:'{"version":"v1","op":"stop","request_id":"req-stop-1","conversation_id":"conv-1","identity":{"user_id":"u-1","tenant_id":"t-1","instance_id":"inst-1","session_id":"sess-1"},"metadata":{"reason":"user_stop"}}\n'
How to interpret stdout events
Proxy emits JSON lines with:
event:assistant_delta|assistant_final|errorop_type: Studio op type passthroughrequest_id,conversation_iddelta,content,complete,metadata,error
Default output mode in this skill is compact (CZ_EMIT_ASSISTANT_DELTAS=false), so in normal OpenClaw use you should expect final/error lines only.
Consumer rule:
- Treat only
op_type == "agent_message"as user-visible answer text. - End of one turn:
event == "assistant_final" && complete == truefor the samerequest_id. errorevent includes structured code:PROTOCOL_ERRORNETWORK_ERRORREMOTE_ERRORREMOTE_TIMEOUT
Operational notes
- The proxy is intentionally serial in one process. Wait for
assistant_final/errorbefore nextuser_input. - If startup connection fails, the proxy writes one
errorevent withrequest_id="startup"and exits non-zero. - If running in a sandboxed tool runtime, ensure required env vars are available in that runtime.