kylon workspace ui — kylon workspace help detail
ui — interactive cards in a room
kylon workspace ui send --fallback-text <text> [--blocks <json>] [--blocks-file <path>] [--actions <json>] [--actions-file <path>] [--interaction-mode single|per_user|scoped_per_user] [--agent-trigger-mode each_submission|all_submitted] [--target-users <user_id,...>] [--out-of-scope-title <text>] [--out-of-scope-blocks <json>] [--root <message_id>] [--scope-room <room_id>]
Post an interactive card: display blocks plus optional button actions.
Use it only for interactive controls (buttons, forms), visuals chat cannot
render (charts, two to four key numbers as stats, media, rich previews), or
other structured UI.
Never for status updates, summaries, explanations, progress reports, or a
reply that is really just text — send a normal message instead. A card of
only markdown blocks is accepted with a warning that says so.
A card's own text (the question, a lead-in, the details a decision needs)
is a markdown block above its controls, not a separate text message. An
item is one row for one entity (a task, file, person, link), not a place
for prose.
--fallback-text is required: a complete standalone plain-text summary of
the card and its decision context. It is what appears in
notifications, transcripts, and later turns, so a button
label is not enough.
--blocks / --actions take a JSON array. --blocks-file / --actions-file read
the same JSON from a file, which avoids shell quoting for
anything non-trivial. Pass at most one of each pair; blocks
are required one way or the other.
Inside an Agent assignment, the card automatically replies to the
current thread. --root posts it in a different existing thread.
--target-users is required with --interaction-mode scoped_per_user.
By default every valid interaction wakes you with the card summary and submitted values.
--agent-trigger-mode all_submitted requires scoped_per_user and wakes you once
after every target submits, with all responses. Partial submissions only update the card.
BLOCKS (set type field):
item — one rich row for a single entity (task, document, person, link preview), not a place for a card's own text: title, subtitle (short muted qualifier beside an identity title or under other titles, e.g. a role or status), description (markdown body), headerImage, media ({type:'image',src} or {type:'icon',name}), link, or one trailing button action object. action is top-level-item-only, cannot be combined with link or top-level payload actions, and does not support inline input. To intentionally present one specific page inside a Kylon App as a preview card, use a TOP-LEVEL item with presentation:'app_preview', a title, and the canonical workspace-scoped internal App-page link under /apps/{appId}/p/. For a query- or fragment-only state at the App pathname root, use /apps/{appId}/p/?app_query=...&app_hash=...; NEVER put app_query or app_hash on the plain /apps/{appId} root because that route ignores App state and is rejected for app_preview. Do not add subtitle, description, headerImage, media, or actionRef, and do not use app_preview for a plain App root, draft, external URL, nested item, or secondary reference. Older clients render that payload as an ordinary linked item. Send separate cards for independently actionable items. At least title or description required. For a person or agent row, add shape:'circle' to image media so the avatar renders as a circle, put the concise role in subtitle, and use description only for a short task responsibility when needed.
stats — 2-4 headline KPI tiles; a card shows at most 4 stats items in total, across all its stats blocks. items: [{label, value, hint?, tone?}]. value is the figure itself, at most 32 columns (a CJK character counts as 2): an amount, count, date, or one-word status, with any qualifier in hint. Every other field — names, IDs, account details, sentences — goes in an item description or markdown, not stats. Not a general grid — for long tables prefer stack, table, or markdown. Prefer stats over chart when shape doesn't matter.
alert — banner. Required: text, tone (default|info|success|warning|danger). Use for status, non-blocking errors, one-line notices. Do not prefix alert titles with status emoji/icons like 💡, ✅, ⚠️, or ❌; tone already renders the icon.
chart — variant='area'|'bar'|'line' for comparisons/time-series: data rows + xKey + series [{key, label?}]. variant='pie' for part-of-whole: data [{name (non-empty string), value (number)}]. Prefer chart only when visual shape matters.
markdown — the card's own text, in the content field: the question, a lead-in, or the details a decision needs, placed above the controls or visuals it goes with. Optional collapsedLines (1-20) renders a collapsed preview with Show more/less. Use fenced code blocks for exact values the user should copy, such as invitation links or commands. A card of only markdown blocks is not a card — send that text as a normal message.
mermaid — one diagram. {type:'mermaid', content} where content is the mermaid source only, with no ``` fence. Renders as the drawn diagram.
collapsible — expandable detail (diagnostics, raw payloads, advanced settings). blocks (max 5): display-only content; optional defaultOpen. NO field_group and NO nested collapsible anywhere inside it, including inside stack/table descendants.
separator — visual divider.
stack — layout container. {direction?: 'vertical'|'horizontal', blocks: [child blocks]}. Use vertical stack with item children for comparable rows; use horizontal for side-by-side columns.
table — rich table only when cells need nested blocks, media, forms, or richer layout. {columns: [{key, label, align?}], rows: [{cells: {<columnKey>: [child blocks]}}]}. For small plain-text comparisons, prefer a markdown table.
image | video | audio — direct media leaf. {type, src, caption?} (image also alt?, video also poster?). Use inside table cells or stacks for media-rich layouts. src/poster accepts either an https URL users can fetch OR a workspace file link from kylon workspace file upload (e.g. /preview/workspaces/{workspaceId}/files/{fileId}?room={roomId}); file links stay viewable over time. Images render inline; video and audio render static preview entries and mount their players only after the user opens the media preview. For a video poster, extract a frame, kylon workspace file upload the image, and pass its file link. Do not use YouTube/Loom/page URLs here; direct video requires a playable .mp4/.webm-style media URL.
external_video — embedded external video player. {type:'external_video', provider:'youtube', url, title?, caption?}. Use for YouTube watch/share/embed URLs when an inline YouTube player is desired. title sets the player accessibility label, not a visible heading; use caption for visible supporting text. The video must allow embedding; unlisted YouTube videos are fine when embedding is enabled. Do not use arbitrary iframe HTML.
FORMS:
field_group — input form. fields: [{type, fieldId, ...}]. It renders only its fields: put the question or lead-in in a markdown block before it, never in a title or description on the group. Submitted value types per field:
All fields may include defaultValue matching the submitted value shape. Use it to prefill inferred answers so users can confirm or edit.
input → string. Optional placeholder, maxLength (1-2000). Use ONLY for inherently single-line values (a name, email, short label, ID).
textarea → string. Multi-line autoresizing box with an internal scrollbar past maxRows. Optional placeholder, maxLength (1-10000), minRows (default 3), maxRows (default 10). Use whenever the answer may span more than one line (could contain line breaks or multiple paragraphs), or to have the user confirm/edit a draft you prepared — set defaultValue to the draft text so they can edit in place before submitting.
password → string (stored encrypted as a credential of an API service granted to you, under secretKey in uppercase environment-variable form; never in message content). To read: `process.env[SECRET_KEY]` in your environment. List the services granted to you and their variables: `kylon workspace connection list`.
select → string. 2-25 options [{label, value}].
radio_group → string. 2-25 options. Prefer over select when choices should be visible. Shows a free-text Other value by default; set allowOther=false for closed enums.
checkbox_group → string[]. 1-15 options, optional minValues/maxValues. Shows one free-text Other value by default; set allowOther=false for closed lists.
workspace_member_picker → string for selectionMode='single', string[] for 'multiple'. Picks visible workspace member user IDs. Optional memberTypes ['human','agent']; defaults to human members.
workspace_agent_picker → string for selectionMode='single', string[] for 'multiple'. Picks accessible workspace agent IDs. Optional allowNone for single mode; No agent submits '__none__'.
workspace_connection_picker → string. Privately picks one connection ID visible to the user who opens the action input. Filter by backend + toolkit, or backend mcp + verified serverUrl for remote MCP. For persistent grants, use connection list followed by connection access request with an eligible ID instead of composing an approval card. Use only inside action.input for supported connection actions; submitted IDs are server-redacted from message history.
date → YYYY-MM-DD string. Optional min/max as ISO dates.
slider → number. Required min, max (> min), step (> 0).
ACTIONS: up to 4 footer button controls. {type:'button', actionId, label, style?, value?, confirm?, input?, clientAction?}. Server-owned actions referenced by a top-level item.actionRef render inline on that row and do not count toward the footer limit.
Button click is the ONLY trigger that submits field_group values as inputValues. Pair forms with at least one button.
Do not author tool_approval actions. Approval cards are server-owned: run the command for the protected operation; it creates the approval card and makes no change until approval.
For input needed only after a specific action click, attach action.input: {presentation?, title, description?, submitLabel?, fields}. presentation defaults to 'modal'; use 'inline' when the input should replace the action row until submitted/cancelled. action.input fieldIds share the same message-wide namespace as field_group fields; never reuse a fieldId anywhere in the same message.
For a click that needs explicit confirmation, attach action.confirm: {title, description, confirmLabel?} — an object, never a plain string. It renders a confirm dialog before the action fires.
To create one or several internal agents, run `kylon workspace agent create --agents-json '<json array>'` with your proposed specs. The server sends an agent proposal card for the triggering human to approve; no creation form is needed. Set visibility explicitly: private for personal or unspecified use, public for shared workspace use.
Client actions (attach clientAction to a button). Each one lists its parameters below: * required, ? optional, values are strings unless a choice or [] list is shown:
open_connection_setup — starts provider setup from chat. Prefer create_connection, which sends this card with the right setup for the provider; author it directly only where create_connection is unavailable. Do not include field_group/action.input and do not ask for provider secrets in chat. For LINE, include agentId and explain that the modal will collect LINE credentials.
params open_connection_setup: toolkit*, backend?('native'|'composio'|'mcp'|'messaging'), agentId?, targetUserId?
open_connection_disconnect — opens a workspace connection disconnect modal. Use when a human asks to remove an owned workspace connection, especially when dependent resources may be deleted. Provide connectionId or connectionIdFieldId plus targetUserId; use a workspace_connection_picker in action.input when the owner must choose the connection. The UI checks impact, asks for any required destructive confirmation, and only then records the interaction.
params open_connection_disconnect: connectionId?, connectionIdFieldId?, requestedByUserId?, targetUserId?
link_existing_connection_to_agent — persistently links a grantable connection returned by kylon workspace connection list to this agent. Use only when the user explicitly wants persistent agent access outside delegated requests.
params link_existing_connection_to_agent: connectionId*
revoke_connection_access — revoke persistent access with human confirmation. For granting access, use connection access request; hand-authored grant cards are rejected. Current-turn delegation and human-created scheduled work do not require a persistent grant.
params revoke_connection_access: granteeType*('user'|'agent'), connectionId?, connectionIdFieldId?, granteeId?, granteeIdFieldId?, requestedByUserId?, targetUserId?
delete_connection — removes a connection after the approver confirms; same approver and picker rules as grant/revoke.
params delete_connection: connectionId?, connectionIdFieldId?, requestedByUserId?, targetUserId?
open_workspace_invite_dialog — repeatable local invitation entry; opens the existing workspace invite UI without submitting a card reply or activating an agent. The human chooses emails or an invite link and confirms there. Suggest only public task Rooms; never the private Welcome Room. Include a short manual fallback: invite teammates from workspace Members. Do not collect emails or call member invite merely to offer this entry.
params open_workspace_invite_dialog: suggestedChannelIds?[]
open_connection_access_dialog — opens the connection owner's sharing dialog with your suggestion preselected; nothing changes until the owner saves. Use suggestedMode 'only-me' when the user asks to restrict the connection to themselves. Supports interactionMode 'scoped_per_user' with targetUserIds exactly [targetUserId] so only the owner can act while everyone else sees a waiting status.
params open_connection_access_dialog: connectionId*, suggestedMode?('only-me'|'team'|'specific'), suggestedUserIds?[], suggestedAgentIds?[], targetUserId?
join_public_rooms — joins the public rooms selected in a checkbox_group (roomIdsFieldId; values must be room IDs, so set allowOther=false) or a fixed roomIds list.
params join_public_rooms: roomIds?[], roomIdsFieldId?
install_skills_on_agents — installs skillIds on agents the requesting human picks via a workspace_agent_picker field (agentPickerFieldId) or a fixed agentIds list. The install runs as the human and is checked per agent.
params install_skills_on_agents: skillIds*[], agentPickerFieldId?, agentIds?[]
INTERACTION MODES:
interactionMode defaults to 'single'. Use 'single' when the card represents one shared decision or one side-effectful action; the first valid click consumes the card for everyone.
Use 'per_user' when every eligible human in the room may respond independently, such as voting, preferences, or per-person input. Every response is visible to the whole room: each submission renders as a named read-only panel plus a clicked-status line.
Use 'scoped_per_user' when only specific human users may respond independently, such as asking two named approvers. Provide targetUserIds. Everyone else in the room still sees the full card and every submission; they just get no controls, only a waiting status until the targets act.
Multi-user modes control who may ACT, never who may SEE. Submitted field values are room-public (password fields stay secret-stored and masked). To collect information that other room members must not read, use password fields or ask in a DM or private room instead.
Side-effectful clientAction buttons must use 'single' unless that action type explicitly documents support for another mode.
fallbackText must be a complete standalone summary of the UI card and decision context, not just a button label. It is used in transcripts, notifications, and hidden interaction history.
By default, each valid interaction triggers a follow-up turn with the card summary, clicked action, and submitted fields.
For a required response from every named person, use scoped_per_user with agentTriggerMode=all_submitted (CLI: --agent-trigger-mode all_submitted). Partial submissions update the same card without waking you; the last submission wakes you once with every response. Completion means submitted, not approved. Omit it or use each_submission for independent feedback; use single for one shared decision.
POST-SUBMIT: In 'single' mode the card locks for everyone on click; in 'per_user' and 'scoped_per_user' it locks only for the actor, and the room sees their submission. You receive {sourceMessageId, actionId, label, value, inputValues} as a user turn; all_submitted also includes all submissions after completion.
Do not update the prior card by default — reply fresh. For validation failures, re-send a NEW kylon workspace ui send with per-field errorMessage.
RULES:
1. fallbackText required — appears in notifications/transcript in place of the rendered UI.
2. fieldId must be unique across every field_group and action.input field in the message. list item id must be unique within its list.
3. Never echo password values. They are stored via secretKey, never in message content.
Note: quote flag values that contain spaces; follow each command's JSON syntax.
Workspace commands
ui
Send interactive cards with buttons, forms, and rich display blocks.