@cecwxf/dsh-room 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.js CHANGED
@@ -37,7 +37,7 @@ var import_react = require("react");
37
37
  var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
38
38
 
39
39
  // src/client/rooms.css
40
- var rooms_default = '/* Collab Rooms UI \u2014 original surface inside the dsh shell. Uses only dsh --dsw-* tokens.\n Independent Rooms surface. Prefix: rm- (rooms). */\n\n.rm-root {\n --rm-accent: var(--dsw-alias-state-business-primary);\n --rm-hover: var(--dsw-alias-interactive-bg-hover);\n --rm-border: var(--dsw-alias-border-l2);\n --rm-bg: var(--dsw-alias-bg-base);\n --rm-panel: var(--dsw-alias-bg-module-platform);\n --rm-label: var(--dsw-alias-label-primary);\n --rm-muted: var(--dsw-alias-label-secondary);\n --rm-quiet: var(--dsw-alias-label-tertiary);\n font: 13px/1.5 var(--dsw-font-family);\n color: var(--rm-label);\n box-sizing: border-box;\n}\n.rm-root *, .rm-root *::before, .rm-root *::after { box-sizing: border-box; }\n.rm-root button, .rm-root input, .rm-root textarea { font: inherit; }\n\n/* \u2500\u2500 sidebar (joined rooms + public search) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-side {\n display: flex; flex-direction: column; height: 100%; min-height: 0;\n padding: 0 8px 8px 0; gap: 8px;\n}\n.rm-side-toolbar {\n display: flex; align-items: center; gap: 6px; flex: none; padding: 4px 0 2px;\n}\n.rm-side-title {\n flex: 1; font-size: 12px; font-weight: 600; letter-spacing: .04em;\n text-transform: uppercase; color: var(--rm-quiet);\n}\n.rm-icon-btn {\n width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 8px; background: transparent; color: var(--rm-muted); cursor: pointer; padding: 0;\n}\n.rm-icon-btn:hover { background: var(--rm-hover); color: var(--rm-label); }\n.rm-search {\n display: flex; align-items: center; gap: 6px; height: 32px; padding: 0 10px;\n border: .5px solid var(--rm-border); border-radius: 8px; background: var(--rm-panel);\n}\n.rm-search input {\n flex: 1; min-width: 0; border: 0; background: transparent; outline: none; color: var(--rm-label); font-size: 13px;\n}\n.rm-search input::placeholder { color: var(--rm-quiet); }\n.rm-side-scroll { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 12px; }\n.rm-group-label { font-size: 11px; font-weight: 600; color: var(--rm-quiet); padding: 0 4px 4px; text-transform: uppercase; letter-spacing: .03em; }\n.rm-room-list { display: flex; flex-direction: column; gap: 2px; }\n.rm-room-item {\n display: flex; align-items: center; gap: 10px; width: 100%; padding: 8px 10px;\n border: 0; border-radius: 10px; background: transparent; color: inherit; cursor: pointer; text-align: left;\n}\n.rm-room-item:hover { background: var(--rm-hover); }\n.rm-room-item[aria-current="true"] { background: var(--rm-hover); }\n.rm-room-item-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }\n.rm-room-item-name { font-weight: 600; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-room-item-meta { font-size: 11.5px; color: var(--rm-quiet); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-room-item-trail { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 2px; }\n.rm-badge {\n display: inline-flex; align-items: center; gap: 3px; height: 18px; padding: 0 6px;\n border-radius: 999px; font-size: 10.5px; font-weight: 500; background: var(--rm-panel); color: var(--rm-muted);\n border: .5px solid var(--rm-border);\n}\n.rm-badge-public { color: var(--rm-accent); border-color: color-mix(in srgb, var(--rm-accent) 35%, transparent); background: color-mix(in srgb, var(--rm-accent) 8%, transparent); }\n.rm-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; background: var(--rm-quiet); }\n.rm-dot-online { background: var(--dsw-alias-state-success-primary, #2bb673); }\n.rm-dot-connecting { background: var(--dsw-alias-state-warn-primary, #e9a23b); }\n.rm-dot-offline { background: var(--dsw-alias-state-error-primary, #e5484d); }\n.rm-empty-hint { padding: 8px 10px; font-size: 12px; color: var(--rm-quiet); line-height: 1.45; }\n.rm-dir-item {\n display: flex; align-items: flex-start; gap: 10px; padding: 10px; border-radius: 10px;\n border: .5px solid var(--rm-border); background: var(--rm-bg); margin-bottom: 6px;\n}\n.rm-dir-item-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }\n.rm-dir-tags { display: flex; flex-wrap: wrap; gap: 4px; }\n.rm-tag { font-size: 11px; color: var(--rm-muted); background: var(--rm-hover); border-radius: 4px; padding: 0 5px; }\n.rm-menu {\n position: fixed; z-index: 1000; min-width: 180px; padding: 4px;\n border-radius: 10px; background: var(--dsw-alias-bg-layer-1, var(--rm-bg));\n border: .5px solid var(--rm-border);\n box-shadow: 0 8px 24px color-mix(in srgb, var(--rm-label) 12%, transparent);\n display: flex; flex-direction: column;\n}\n.rm-menu button {\n display: flex; align-items: center; gap: 8px; height: 32px; padding: 0 10px;\n border: 0; border-radius: 6px; background: transparent; color: var(--rm-label); cursor: pointer; text-align: left;\n}\n.rm-menu button:hover { background: var(--rm-hover); }\n\n/* \u2500\u2500 main: room workspace \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-main { display: flex; flex: 1; min-height: 0; height: 100%; width: 100%; background: var(--rm-bg); }\n.rm-workspace { display: flex; flex: 1; min-width: 0; min-height: 0; }\n.rm-chat { display: flex; flex-direction: column; flex: 1; min-width: 0; min-height: 0; }\n.rm-chat-header {\n flex: none; display: flex; align-items: center; gap: 10px; height: 48px;\n padding: 0 16px; border-bottom: .5px solid var(--rm-border);\n}\n.rm-chat-header-title { font-size: 14px; font-weight: 600; }\n.rm-chat-header-sub { font-size: 12px; color: var(--rm-quiet); }\n.rm-messages {\n flex: 1; min-height: 0; overflow: auto; padding: 16px 18px 8px;\n display: flex; flex-direction: column; gap: 2px;\n}\n.rm-day {\n align-self: center; margin: 10px 0 12px; padding: 2px 10px; border-radius: 999px;\n font-size: 11px; color: var(--rm-quiet); background: var(--dsw-alias-button-elevated-fill, var(--rm-panel));\n}\n.rm-msg {\n display: grid; grid-template-columns: 32px 1fr; gap: 0 10px; padding: 4px 6px; border-radius: 8px;\n max-width: 720px;\n}\n.rm-msg:hover { background: color-mix(in srgb, var(--rm-hover) 60%, transparent); }\n.rm-msg-self { margin-left: auto; grid-template-columns: 1fr; max-width: 640px; }\n.rm-msg-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 1px; }\n.rm-msg-author { font-size: 12px; font-weight: 600; color: var(--rm-muted); }\n.rm-msg-time { font-size: 11px; color: var(--rm-quiet); }\n.rm-msg-body { font-size: 13.5px; line-height: 1.55; white-space: pre-wrap; word-break: break-word; }\n.rm-msg-self .rm-msg-body {\n background: var(--dsw-alias-label-primary); color: var(--dsw-alias-label-primary-foreground);\n padding: 8px 12px; border-radius: 12px 12px 4px 12px; width: fit-content; margin-left: auto;\n}\n.rm-msg-event {\n grid-template-columns: 1fr; max-width: 560px; margin: 6px 0;\n padding: 8px 12px; border: .5px solid var(--rm-border); border-radius: 10px; background: var(--rm-panel);\n}\n.rm-mention { color: var(--rm-accent); font-weight: 500; }\n.rm-msg-self .rm-mention { color: inherit; text-decoration: underline; text-underline-offset: 2px; }\n.rm-task-card { display: flex; flex-direction: column; gap: 4px; }\n.rm-task-head { display: flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--rm-muted); }\n.rm-task-title { font-weight: 600; font-size: 13.5px; }\n.rm-composer {\n flex: none; display: flex; align-items: flex-end; gap: 8px; padding: 10px 16px 14px;\n border-top: .5px solid var(--rm-border);\n}\n.rm-composer-box {\n flex: 1; display: flex; align-items: flex-end; gap: 6px; min-height: 44px;\n padding: 8px 10px; border: .5px solid var(--rm-border); border-radius: 12px; background: var(--rm-panel);\n}\n.rm-composer-box:focus-within { border-color: color-mix(in srgb, var(--rm-accent) 50%, var(--rm-border)); }\n.rm-composer-box textarea {\n flex: 1; min-height: 28px; max-height: 120px; border: 0; background: transparent; outline: none;\n resize: none; color: var(--rm-label); line-height: 1.45;\n}\n.rm-composer-box textarea::placeholder { color: var(--rm-quiet); }\n.rm-composer-mode {\n width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 8px; background: transparent; color: var(--rm-muted); cursor: pointer; flex: none;\n}\n.rm-composer-mode:hover, .rm-composer-mode[aria-pressed="true"] {\n color: var(--rm-accent); background: color-mix(in srgb, var(--rm-accent) 12%, transparent);\n}\n.rm-chat-hint { margin: 48px auto; text-align: center; color: var(--rm-quiet); font-size: 13px; max-width: 360px; line-height: 1.5; }\n\n/* \u2500\u2500 detail column (members / invite / about) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-detail {\n flex: 0 0 280px; width: 280px; display: flex; flex-direction: column; min-height: 0;\n border-left: .5px solid var(--rm-border); background: var(--rm-bg); overflow: auto;\n}\n.rm-detail-section { padding: 14px 14px 8px; border-bottom: .5px solid var(--rm-border); }\n.rm-detail-section:last-child { border-bottom: 0; }\n.rm-detail-h {\n display: flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 600;\n text-transform: uppercase; letter-spacing: .04em; color: var(--rm-quiet); margin-bottom: 8px;\n}\n.rm-member {\n display: flex; align-items: center; gap: 8px; padding: 5px 4px; border-radius: 8px; min-height: 34px;\n}\n.rm-member:hover { background: var(--rm-hover); }\n.rm-member-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }\n.rm-member-actions { display: none; gap: 2px; }\n.rm-member:hover .rm-member-actions, .rm-member:focus-within .rm-member-actions { display: inline-flex; }\n.rm-mini {\n width: 24px; height: 24px; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 6px; background: transparent; color: var(--rm-muted); cursor: pointer; font-size: 12px;\n}\n.rm-mini:hover { background: var(--dsw-alias-interactive-bg-active); color: var(--rm-label); }\n.rm-task-row { display: flex; align-items: center; gap: 6px; padding: 4px; font-size: 12.5px; }\n.rm-task-row-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-about { display: grid; grid-template-columns: 88px 1fr; gap: 6px 8px; font-size: 12.5px; margin: 0; }\n.rm-about dt { color: var(--rm-quiet); margin: 0; }\n.rm-about dd { margin: 0; min-width: 0; overflow-wrap: anywhere; display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }\n.rm-mono { font-family: var(--dsw-font-markdown-code-block-font-family, ui-monospace, monospace); font-size: 11.5px; }\n.rm-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }\n.rm-danger { color: var(--dsw-alias-state-error-primary, #e5484d) !important; }\n.rm-note { font-size: 12px; color: var(--rm-quiet); line-height: 1.45; margin: 0 0 8px; }\n\n/* \u2500\u2500 empty / welcome \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-welcome-wrap {\n flex: 1; display: flex; align-items: center; justify-content: center; padding: 32px; background: var(--rm-bg);\n}\n.rm-welcome {\n width: min(420px, 100%); display: flex; flex-direction: column; gap: 10px;\n padding: 20px; border-radius: 14px; border: .5px solid var(--rm-border); background: var(--rm-panel);\n}\n.rm-welcome h1 { margin: 0; font-size: 16px; font-weight: 600; }\n.rm-welcome p { margin: 0; font-size: 13px; color: var(--rm-muted); line-height: 1.5; }\n.rm-welcome-actions { display: flex; flex-direction: column; gap: 6px; margin-top: 6px; }\n.rm-welcome-action {\n display: flex; align-items: flex-start; gap: 10px; width: 100%; padding: 10px 12px;\n border: .5px solid var(--rm-border); border-radius: 10px; background: var(--rm-bg);\n color: var(--rm-label); cursor: pointer; text-align: left;\n}\n.rm-welcome-action:hover { background: var(--rm-hover); }\n.rm-welcome-action b { display: block; font-size: 13px; }\n.rm-welcome-action span { display: block; font-size: 12px; color: var(--rm-quiet); margin-top: 2px; }\n\n/* \u2500\u2500 avatars \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-avatar {\n display: inline-flex; align-items: center; justify-content: center; flex: none;\n font-weight: 600; letter-spacing: .02em; user-select: none;\n}\n.rm-avatar-wrap { position: relative; display: inline-flex; flex: none; }\n\n/* \u2500\u2500 dialogs (inside dsh Modal) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-form { display: flex; flex-direction: column; gap: 14px; }\n.rm-field { display: flex; flex-direction: column; gap: 6px; }\n.rm-field-label { font-size: 13px; font-weight: 500; }\n.rm-field-hint { font-size: 12px; color: var(--rm-quiet); }\n.rm-name-row { display: flex; align-items: center; gap: 10px; }\n.rm-name-row > :last-child { flex: 1; }\n.rm-switch-row {\n display: flex; align-items: center; justify-content: space-between; gap: 14px;\n padding: 10px 12px; border-radius: 10px; background: var(--rm-panel);\n}\n.rm-error { color: var(--dsw-alias-state-error-primary, #e5484d); font-size: 12.5px; }\n.rm-invite {\n display: block; padding: 10px 12px; border-radius: 8px; background: var(--rm-panel);\n font-family: var(--dsw-font-markdown-code-block-font-family, ui-monospace, monospace);\n font-size: 12px; word-break: break-all; user-select: all;\n}\n\n/* Dual transport + approvals + agent config */\n.rm-mode-row { display: flex; gap: 8px; }\n.rm-mode-btn {\n flex: 1; padding: 10px 12px; border-radius: 10px; border: 1px solid var(--rm-border, #2a2f3a);\n background: transparent; color: inherit; cursor: pointer; font: inherit;\n}\n.rm-mode-btn.is-on { border-color: var(--rm-accent, #5b8cff); background: color-mix(in srgb, var(--rm-accent, #5b8cff) 16%, transparent); }\n.rm-lan-browse { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; align-items: center; }\n.rm-lan-peer {\n font-size: 12px; padding: 4px 8px; border-radius: 999px; border: 1px solid var(--rm-border, #2a2f3a);\n background: transparent; color: inherit; cursor: pointer;\n}\n.rm-approvals { display: flex; flex-direction: column; gap: 8px; padding: 10px 12px; border-bottom: 1px solid var(--rm-border, #2a2f3a); background: color-mix(in srgb, #f5a524 12%, transparent); }\n.rm-approval { display: flex; gap: 12px; align-items: center; justify-content: space-between; }\n.rm-approval-body { font-size: 13px; }\n.rm-approval-actions { display: flex; gap: 6px; flex-shrink: 0; }\n.rm-input { width: 100%; padding: 8px 10px; border-radius: 8px; border: 1px solid var(--rm-border, #2a2f3a); background: transparent; color: inherit; font: inherit; }\n\n.rm-ws-bar { display: flex; flex-direction: column; gap: 4px; padding: 8px 12px; border-bottom: 1px solid var(--rm-border, #2a2f3a); }\n.rm-ws-chip { display: inline-flex; align-items: center; gap: 6px; align-self: flex-start; padding: 4px 10px; border-radius: 999px; border: 1px solid var(--rm-border, #2a2f3a); background: transparent; color: inherit; cursor: pointer; font: inherit; font-size: 12.5px; }\n.rm-ws-path { font-size: 11px; color: var(--rm-quiet, #8b93a7); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-room-item.is-active { background: color-mix(in srgb, var(--rm-accent, #5b8cff) 12%, transparent); }\n\n/* Native-aligned room composer (mirrors dsh conversation control row) */\n.rm-composer-card {\n box-sizing: border-box;\n width: 100%;\n max-width: 920px;\n margin: 0 auto;\n border-radius: 16px;\n background: var(--rm-elev, var(--rm-panel, #12151c));\n border: 1px solid var(--rm-border, #2a2f3a);\n box-shadow: 0 1px 2px rgba(0,0,0,.06);\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 8px 0 6px;\n}\n.rm-composer-grow { position: relative; padding: 0 4px; }\n.rm-composer-grow textarea {\n box-sizing: border-box; width: 100%; min-height: 36px; max-height: 160px;\n resize: none; border: none; outline: none; background: transparent;\n color: inherit; font: inherit; font-size: 14px; line-height: 22px;\n padding: 4px 12px 0 14px;\n}\n.rm-composer-grow textarea:disabled { opacity: .55; cursor: not-allowed; }\n.rm-composer-row {\n display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;\n gap: 10px; min-width: 0; padding: 2px 8px 4px;\n}\n.rm-composer-tools, .rm-composer-modes, .rm-composer-trailing {\n display: flex; align-items: center; min-width: 0; gap: 8px;\n}\n.rm-composer-trailing { flex: none; margin-left: auto; }\n.rm-composer-add {\n width: 28px; height: 28px; border: none; border-radius: 999px;\n background: color-mix(in srgb, var(--rm-fg, #e8eaed) 8%, transparent);\n color: inherit; cursor: pointer; display: grid; place-items: center;\n}\n.rm-composer-add:hover, .rm-composer-add[aria-pressed="true"] {\n background: color-mix(in srgb, var(--rm-accent, #5b8cff) 22%, transparent);\n}\n.rm-composer-select {\n max-width: 220px; height: 28px; border: none; border-radius: 8px;\n background: transparent; color: var(--rm-quiet, #8b93a7);\n font: inherit; font-size: 13px; font-weight: 500; line-height: 20px;\n padding: 0 8px; cursor: pointer; white-space: nowrap;\n}\n.rm-composer-select:hover:not(:disabled) {\n background: color-mix(in srgb, var(--rm-fg, #e8eaed) 8%, transparent);\n}\n.rm-composer-select:disabled { opacity: .5; cursor: default; }\n.rm-composer-model-text { display: inline; }\n.rm-composer-model-icon { display: none; font-size: 10px; }\n.rm-composer-effort {\n margin-left: 6px; font-size: 11px; opacity: .75;\n text-transform: uppercase; letter-spacing: .02em;\n}\n.rm-composer-send {\n width: 34px; height: 34px; border: none; border-radius: 999px;\n background: var(--rm-accent, #5b8cff); color: #fff; cursor: pointer;\n display: grid; place-items: center; transform: translateY(-2px);\n}\n.rm-composer-send:hover:not(:disabled) { filter: brightness(1.08); }\n.rm-composer-send:disabled { opacity: .4; cursor: default; }\n.rm-composer-popwrap { position: relative; }\n.rm-composer-menu {\n position: absolute; bottom: calc(100% + 6px); left: 0; z-index: 40;\n min-width: 200px; max-height: 320px; overflow: auto;\n border: 1px solid var(--rm-border, #2a2f3a); border-radius: 12px;\n background: var(--rm-elev, #12151c); box-shadow: 0 8px 24px rgba(0,0,0,.28);\n padding: 6px; display: flex; flex-direction: column; gap: 2px;\n}\n.rm-composer-menu-wide { min-width: 280px; max-width: 340px; }\n.rm-composer-menu-label {\n font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em;\n color: var(--rm-quiet, #8b93a7); padding: 6px 8px 2px;\n}\n.rm-composer-menu button[role="menuitem"] {\n text-align: left; border: none; background: transparent; color: inherit;\n font: inherit; font-size: 13px; padding: 6px 8px; border-radius: 8px; cursor: pointer;\n}\n.rm-composer-menu button[role="menuitem"]:hover,\n.rm-composer-menu button[role="menuitem"].is-active {\n background: color-mix(in srgb, var(--rm-accent, #5b8cff) 16%, transparent);\n}\n.rm-composer-check {\n display: flex; align-items: center; gap: 8px; font-size: 13px; padding: 4px 8px; cursor: pointer;\n}\n.rm-grant-row {\n display: flex; align-items: center; justify-content: space-between; gap: 8px;\n padding: 2px 8px; font-size: 12.5px;\n}\n.rm-grant-row select {\n font: inherit; font-size: 12px; border-radius: 6px;\n border: 1px solid var(--rm-border, #2a2f3a); background: transparent; color: inherit; padding: 2px 6px;\n}\n.rm-peer-grant-block { border-top: 1px solid var(--rm-border, #2a2f3a); padding-top: 4px; margin-top: 4px; }\n.rm-peer-grant-name { font-size: 12px; font-weight: 600; padding: 4px 8px; overflow: hidden; text-overflow: ellipsis; }\n\n/* Hide legacy tight composer if still referenced */\n.rm-composer { display: none; }\n\n.rm-composer-grow { position: relative; }\n.rm-mention-menu {\n position: absolute; left: 8px; right: 8px; bottom: calc(100% + 6px); z-index: 50;\n max-height: 240px; overflow: auto;\n border: 1px solid var(--rm-border, #2a2f3a); border-radius: 12px;\n background: var(--rm-elev, #12151c); box-shadow: 0 10px 28px rgba(0,0,0,.32);\n padding: 4px; display: flex; flex-direction: column; gap: 2px;\n}\n.rm-mention-item {\n display: flex; align-items: center; gap: 8px; text-align: left;\n border: none; background: transparent; color: inherit; font: inherit;\n padding: 6px 8px; border-radius: 8px; cursor: pointer;\n}\n.rm-mention-item:hover, .rm-mention-item.is-active {\n background: color-mix(in srgb, var(--rm-accent, #5b8cff) 18%, transparent);\n}\n.rm-mention-item-body { display: flex; flex-direction: column; min-width: 0; flex: 1; }\n.rm-mention-item-name { font-size: 13px; font-weight: 600; }\n.rm-mention-item-meta { font-size: 11px; color: var(--rm-quiet, #8b93a7); }\n\n\n/* New Room shortcut under workspace (mirrors New Session affordance) */\n.rm-new-room {\n display: flex; align-items: center; justify-content: center; gap: 6px;\n width: 100%; height: 36px; margin-top: 8px;\n border: .5px solid var(--rm-border); border-radius: 10px;\n background: var(--dsw-alias-button-elevated-fill, var(--rm-panel));\n color: var(--rm-label); cursor: pointer; font-weight: 500; font-size: 13.5px;\n}\n.rm-new-room:hover { background: var(--dsw-alias-button-floating-hover, var(--rm-hover)); }\n.rm-new-room svg { flex: none; }\n\n/* Wide / landscape create + browse modals (overrides default 380px Modal card) */\n.rm-modal-wide {\n width: min(720px, calc(100vw - 48px)) !important;\n max-height: min(640px, calc(100vh - 48px));\n}\n.rm-form-wide {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px 18px;\n align-items: start;\n}\n.rm-form-wide .rm-span-2 { grid-column: 1 / -1; }\n.rm-browse { display: flex; flex-direction: column; gap: 12px; min-height: 280px; max-height: 420px; }\n.rm-browse-search { flex: none; }\n.rm-browse-status { font-size: 11.5px; color: var(--rm-quiet); white-space: nowrap; }\n.rm-browse-list { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 6px; }\n@media (max-width: 640px) {\n .rm-form-wide { grid-template-columns: 1fr; }\n .rm-modal-wide { width: min(420px, calc(100vw - 32px)) !important; }\n}\n\n\n/* \u2500\u2500 panel layout: rooms nav (grouped by native dsh workspace) + content \u2500\u2500 */\n.rm-main { flex-direction: row; }\n.rm-main-body { display: flex; flex-direction: column; flex: 1; min-width: 0; min-height: 0; }\n.rm-main-body > .rm-welcome-wrap { flex: 1; }\n.rm-main-body > .rm-workspace { flex: 1; }\n.rm-nav {\n flex: 0 0 248px; width: 248px; display: flex; flex-direction: column; gap: 8px; min-height: 0;\n padding: 10px 8px; border-right: .5px solid var(--rm-border); background: var(--rm-bg);\n}\n.rm-nav-head { display: flex; align-items: center; gap: 4px; padding: 0 4px; }\n.rm-nav-scroll { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 10px; }\n.rm-ws-group { display: flex; flex-direction: column; gap: 2px; }\n.rm-ws-head {\n display: flex; align-items: center; gap: 6px; height: 30px; padding: 0 2px 0 8px;\n font-size: 12.5px; font-weight: 600; color: var(--rm-muted);\n}\n.rm-ws-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-ws-add { width: 24px; height: 24px; }\n.rm-ws-empty { padding: 2px 10px 6px 30px; font-size: 11.5px; }\n.rm-nav .rm-room-item { padding: 6px 8px 6px 14px; }\n.rm-session-mark { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; color: var(--rm-accent, var(--dsw-alias-state-business-primary)); }\n';
40
+ var rooms_default = '/* Collab Rooms UI \u2014 original surface inside the dsh shell. Uses only dsh --dsw-* tokens.\n Independent Rooms surface. Prefix: rm- (rooms). */\n\n.rm-root {\n --rm-accent: var(--dsw-alias-state-business-primary);\n --rm-hover: var(--dsw-alias-interactive-bg-hover);\n --rm-border: var(--dsw-alias-border-l2);\n --rm-bg: var(--dsw-alias-bg-base);\n --rm-panel: var(--dsw-alias-bg-module-platform);\n --rm-label: var(--dsw-alias-label-primary);\n --rm-muted: var(--dsw-alias-label-secondary);\n --rm-quiet: var(--dsw-alias-label-tertiary);\n font: 13px/1.5 var(--dsw-font-family);\n color: var(--rm-label);\n box-sizing: border-box;\n}\n.rm-root *, .rm-root *::before, .rm-root *::after { box-sizing: border-box; }\n.rm-root button, .rm-root input, .rm-root textarea { font: inherit; }\n\n/* \u2500\u2500 sidebar (joined rooms + public search) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-side {\n display: flex; flex-direction: column; height: 100%; min-height: 0;\n padding: 0 8px 8px 0; gap: 8px;\n}\n.rm-side-toolbar {\n display: flex; align-items: center; gap: 6px; flex: none; padding: 4px 0 2px;\n}\n.rm-side-title {\n flex: 1; font-size: 12px; font-weight: 600; letter-spacing: .04em;\n text-transform: uppercase; color: var(--rm-quiet);\n}\n.rm-icon-btn {\n width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 8px; background: transparent; color: var(--rm-muted); cursor: pointer; padding: 0;\n}\n.rm-icon-btn:hover { background: var(--rm-hover); color: var(--rm-label); }\n.rm-search {\n display: flex; align-items: center; gap: 6px; height: 32px; padding: 0 10px;\n border: .5px solid var(--rm-border); border-radius: 8px; background: var(--rm-panel);\n}\n.rm-search input {\n flex: 1; min-width: 0; border: 0; background: transparent; outline: none; color: var(--rm-label); font-size: 13px;\n}\n.rm-search input::placeholder { color: var(--rm-quiet); }\n.rm-side-scroll { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 12px; }\n.rm-group-label { font-size: 11px; font-weight: 600; color: var(--rm-quiet); padding: 0 4px 4px; text-transform: uppercase; letter-spacing: .03em; }\n.rm-room-list { display: flex; flex-direction: column; gap: 2px; }\n.rm-room-item {\n display: flex; align-items: center; gap: 10px; width: 100%; padding: 8px 10px;\n border: 0; border-radius: 10px; background: transparent; color: inherit; cursor: pointer; text-align: left;\n}\n.rm-room-item:hover { background: var(--rm-hover); }\n.rm-room-item[aria-current="true"] { background: var(--rm-hover); }\n.rm-room-item-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }\n.rm-room-item-name { font-weight: 600; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-room-item-meta { font-size: 11.5px; color: var(--rm-quiet); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-room-item-trail { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 2px; }\n.rm-badge {\n display: inline-flex; align-items: center; gap: 3px; height: 18px; padding: 0 6px;\n border-radius: 999px; font-size: 10.5px; font-weight: 500; background: var(--rm-panel); color: var(--rm-muted);\n border: .5px solid var(--rm-border);\n}\n.rm-badge-public { color: var(--rm-accent); border-color: color-mix(in srgb, var(--rm-accent) 35%, transparent); background: color-mix(in srgb, var(--rm-accent) 8%, transparent); }\n.rm-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; background: var(--rm-quiet); }\n.rm-dot-online { background: var(--dsw-alias-state-success-primary, #2bb673); }\n.rm-dot-connecting { background: var(--dsw-alias-state-warn-primary, #e9a23b); }\n.rm-dot-offline { background: var(--dsw-alias-state-error-primary, #e5484d); }\n.rm-empty-hint { padding: 8px 10px; font-size: 12px; color: var(--rm-quiet); line-height: 1.45; }\n.rm-dir-item {\n display: flex; align-items: flex-start; gap: 10px; padding: 10px; border-radius: 10px;\n border: .5px solid var(--rm-border); background: var(--rm-bg); margin-bottom: 6px;\n}\n.rm-dir-item-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }\n.rm-dir-tags { display: flex; flex-wrap: wrap; gap: 4px; }\n.rm-tag { font-size: 11px; color: var(--rm-muted); background: var(--rm-hover); border-radius: 4px; padding: 0 5px; }\n.rm-menu {\n position: fixed; z-index: 1000; min-width: 180px; padding: 4px;\n border-radius: 10px; background: var(--dsw-alias-bg-layer-1, var(--rm-bg));\n border: .5px solid var(--rm-border);\n box-shadow: 0 8px 24px color-mix(in srgb, var(--rm-label) 12%, transparent);\n display: flex; flex-direction: column;\n}\n.rm-menu button {\n display: flex; align-items: center; gap: 8px; height: 32px; padding: 0 10px;\n border: 0; border-radius: 6px; background: transparent; color: var(--rm-label); cursor: pointer; text-align: left;\n}\n.rm-menu button:hover { background: var(--rm-hover); }\n\n/* \u2500\u2500 main: room workspace \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-main { display: flex; flex: 1; min-height: 0; height: 100%; width: 100%; background: var(--rm-bg); }\n.rm-workspace { display: flex; flex: 1; min-width: 0; min-height: 0; }\n.rm-chat { display: flex; flex-direction: column; flex: 1; min-width: 0; min-height: 0; }\n.rm-chat-header {\n flex: none; display: flex; align-items: center; gap: 10px; height: 48px;\n padding: 0 16px; border-bottom: .5px solid var(--rm-border);\n}\n.rm-chat-header-title { font-size: 14px; font-weight: 600; }\n.rm-chat-header-sub { font-size: 12px; color: var(--rm-quiet); }\n.rm-messages {\n flex: 1; min-height: 0; overflow: auto; padding: 16px 18px 8px;\n display: flex; flex-direction: column; gap: 2px;\n}\n.rm-day {\n align-self: center; margin: 10px 0 12px; padding: 2px 10px; border-radius: 999px;\n font-size: 11px; color: var(--rm-quiet); background: var(--dsw-alias-button-elevated-fill, var(--rm-panel));\n}\n.rm-msg {\n display: grid; grid-template-columns: 32px 1fr; gap: 0 10px; padding: 4px 6px; border-radius: 8px;\n max-width: 720px;\n}\n.rm-msg:hover { background: color-mix(in srgb, var(--rm-hover) 60%, transparent); }\n.rm-msg-self { margin-left: auto; grid-template-columns: 1fr; max-width: 640px; }\n.rm-msg-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 1px; }\n.rm-msg-author { font-size: 12px; font-weight: 600; color: var(--rm-muted); }\n.rm-msg-time { font-size: 11px; color: var(--rm-quiet); }\n.rm-msg-body { font-size: 13.5px; line-height: 1.55; white-space: pre-wrap; word-break: break-word; }\n.rm-msg-self .rm-msg-body {\n background: var(--dsw-alias-label-primary); color: var(--dsw-alias-label-primary-foreground);\n padding: 8px 12px; border-radius: 12px 12px 4px 12px; width: fit-content; margin-left: auto;\n}\n.rm-msg-event {\n grid-template-columns: 1fr; max-width: 560px; margin: 6px 0;\n padding: 8px 12px; border: .5px solid var(--rm-border); border-radius: 10px; background: var(--rm-panel);\n}\n.rm-mention { color: var(--rm-accent); font-weight: 500; }\n.rm-msg-self .rm-mention { color: inherit; text-decoration: underline; text-underline-offset: 2px; }\n.rm-task-card { display: flex; flex-direction: column; gap: 4px; }\n.rm-task-head { display: flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--rm-muted); }\n.rm-task-title { font-weight: 600; font-size: 13.5px; }\n.rm-composer {\n flex: none; display: flex; align-items: flex-end; gap: 8px; padding: 10px 16px 14px;\n border-top: .5px solid var(--rm-border);\n}\n.rm-composer-box {\n flex: 1; display: flex; align-items: flex-end; gap: 6px; min-height: 44px;\n padding: 8px 10px; border: .5px solid var(--rm-border); border-radius: 12px; background: var(--rm-panel);\n}\n.rm-composer-box:focus-within { border-color: color-mix(in srgb, var(--rm-accent) 50%, var(--rm-border)); }\n.rm-composer-box textarea {\n flex: 1; min-height: 28px; max-height: 120px; border: 0; background: transparent; outline: none;\n resize: none; color: var(--rm-label); line-height: 1.45;\n}\n.rm-composer-box textarea::placeholder { color: var(--rm-quiet); }\n.rm-composer-mode {\n width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 8px; background: transparent; color: var(--rm-muted); cursor: pointer; flex: none;\n}\n.rm-composer-mode:hover, .rm-composer-mode[aria-pressed="true"] {\n color: var(--rm-accent); background: color-mix(in srgb, var(--rm-accent) 12%, transparent);\n}\n.rm-chat-hint { margin: 48px auto; text-align: center; color: var(--rm-quiet); font-size: 13px; max-width: 360px; line-height: 1.5; }\n\n/* \u2500\u2500 detail column (members / invite / about) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-detail {\n flex: 0 0 280px; width: 280px; display: flex; flex-direction: column; min-height: 0;\n border-left: .5px solid var(--rm-border); background: var(--rm-bg); overflow: auto;\n}\n.rm-detail-section { padding: 14px 14px 8px; border-bottom: .5px solid var(--rm-border); }\n.rm-detail-section:last-child { border-bottom: 0; }\n.rm-detail-h {\n display: flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 600;\n text-transform: uppercase; letter-spacing: .04em; color: var(--rm-quiet); margin-bottom: 8px;\n}\n.rm-member {\n display: flex; align-items: center; gap: 8px; padding: 5px 4px; border-radius: 8px; min-height: 34px;\n}\n.rm-member:hover { background: var(--rm-hover); }\n.rm-member-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }\n.rm-member-actions { display: none; gap: 2px; }\n.rm-member:hover .rm-member-actions, .rm-member:focus-within .rm-member-actions { display: inline-flex; }\n.rm-mini {\n width: 24px; height: 24px; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 6px; background: transparent; color: var(--rm-muted); cursor: pointer; font-size: 12px;\n}\n.rm-mini:hover { background: var(--dsw-alias-interactive-bg-active); color: var(--rm-label); }\n.rm-task-row { display: flex; align-items: center; gap: 6px; padding: 4px; font-size: 12.5px; }\n.rm-task-row-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-about { display: grid; grid-template-columns: 88px 1fr; gap: 6px 8px; font-size: 12.5px; margin: 0; }\n.rm-about dt { color: var(--rm-quiet); margin: 0; }\n.rm-about dd { margin: 0; min-width: 0; overflow-wrap: anywhere; display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }\n.rm-mono { font-family: var(--dsw-font-markdown-code-block-font-family, ui-monospace, monospace); font-size: 11.5px; }\n.rm-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }\n.rm-danger { color: var(--dsw-alias-state-error-primary, #e5484d) !important; }\n.rm-note { font-size: 12px; color: var(--rm-quiet); line-height: 1.45; margin: 0 0 8px; }\n\n/* \u2500\u2500 empty / welcome \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-welcome-wrap {\n flex: 1; display: flex; align-items: center; justify-content: center; padding: 32px; background: var(--rm-bg);\n}\n.rm-welcome {\n width: min(420px, 100%); display: flex; flex-direction: column; gap: 10px;\n padding: 20px; border-radius: 14px; border: .5px solid var(--rm-border); background: var(--rm-panel);\n}\n.rm-welcome h1 { margin: 0; font-size: 16px; font-weight: 600; }\n.rm-welcome p { margin: 0; font-size: 13px; color: var(--rm-muted); line-height: 1.5; }\n.rm-welcome-actions { display: flex; flex-direction: column; gap: 6px; margin-top: 6px; }\n.rm-welcome-action {\n display: flex; align-items: flex-start; gap: 10px; width: 100%; padding: 10px 12px;\n border: .5px solid var(--rm-border); border-radius: 10px; background: var(--rm-bg);\n color: var(--rm-label); cursor: pointer; text-align: left;\n}\n.rm-welcome-action:hover { background: var(--rm-hover); }\n.rm-welcome-action b { display: block; font-size: 13px; }\n.rm-welcome-action span { display: block; font-size: 12px; color: var(--rm-quiet); margin-top: 2px; }\n\n/* \u2500\u2500 avatars \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-avatar {\n display: inline-flex; align-items: center; justify-content: center; flex: none;\n font-weight: 600; letter-spacing: .02em; user-select: none;\n}\n.rm-avatar-wrap { position: relative; display: inline-flex; flex: none; }\n\n/* \u2500\u2500 dialogs (inside dsh Modal) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-form { display: flex; flex-direction: column; gap: 14px; }\n.rm-field { display: flex; flex-direction: column; gap: 6px; }\n.rm-field-label { font-size: 13px; font-weight: 500; }\n.rm-field-hint { font-size: 12px; color: var(--rm-quiet); }\n.rm-name-row { display: flex; align-items: center; gap: 10px; }\n.rm-name-row > :last-child { flex: 1; }\n.rm-switch-row {\n display: flex; align-items: center; justify-content: space-between; gap: 14px;\n padding: 10px 12px; border-radius: 10px; background: var(--rm-panel);\n}\n.rm-error { color: var(--dsw-alias-state-error-primary, #e5484d); font-size: 12.5px; }\n.rm-invite {\n display: block; padding: 10px 12px; border-radius: 8px; background: var(--rm-panel);\n font-family: var(--dsw-font-markdown-code-block-font-family, ui-monospace, monospace);\n font-size: 12px; word-break: break-all; user-select: all;\n}\n\n/* Dual transport + approvals + agent config */\n.rm-mode-row { display: flex; gap: 8px; }\n.rm-mode-btn {\n flex: 1; padding: 10px 12px; border-radius: 10px; border: 1px solid var(--rm-border, #2a2f3a);\n background: transparent; color: inherit; cursor: pointer; font: inherit;\n}\n.rm-mode-btn.is-on { border-color: var(--rm-accent, #5b8cff); background: color-mix(in srgb, var(--rm-accent, #5b8cff) 16%, transparent); }\n.rm-lan-browse { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; align-items: center; }\n.rm-lan-peer {\n font-size: 12px; padding: 4px 8px; border-radius: 999px; border: 1px solid var(--rm-border, #2a2f3a);\n background: transparent; color: inherit; cursor: pointer;\n}\n.rm-approvals { display: flex; flex-direction: column; gap: 8px; padding: 10px 12px; border-bottom: 1px solid var(--rm-border, #2a2f3a); background: color-mix(in srgb, #f5a524 12%, transparent); }\n.rm-approval { display: flex; gap: 12px; align-items: center; justify-content: space-between; }\n.rm-approval-body { font-size: 13px; }\n.rm-approval-actions { display: flex; gap: 6px; flex-shrink: 0; }\n.rm-input { width: 100%; padding: 8px 10px; border-radius: 8px; border: 1px solid var(--rm-border, #2a2f3a); background: transparent; color: inherit; font: inherit; }\n\n.rm-ws-bar { display: flex; flex-direction: column; gap: 4px; padding: 8px 12px; border-bottom: 1px solid var(--rm-border, #2a2f3a); }\n.rm-ws-chip { display: inline-flex; align-items: center; gap: 6px; align-self: flex-start; padding: 4px 10px; border-radius: 999px; border: 1px solid var(--rm-border, #2a2f3a); background: transparent; color: inherit; cursor: pointer; font: inherit; font-size: 12.5px; }\n.rm-ws-path { font-size: 11px; color: var(--rm-quiet, #8b93a7); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-room-item.is-active { background: color-mix(in srgb, var(--rm-accent, #5b8cff) 12%, transparent); }\n\n/* Native-aligned room composer (mirrors dsh conversation control row) */\n.rm-composer-card {\n box-sizing: border-box;\n width: 100%;\n max-width: 920px;\n margin: 0 auto;\n border-radius: 16px;\n background: var(--rm-elev, var(--rm-panel, #12151c));\n border: 1px solid var(--rm-border, #2a2f3a);\n box-shadow: 0 1px 2px rgba(0,0,0,.06);\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 8px 0 6px;\n}\n.rm-composer-grow { position: relative; padding: 0 4px; }\n.rm-composer-grow textarea {\n box-sizing: border-box; width: 100%; min-height: 36px; max-height: 160px;\n resize: none; border: none; outline: none; background: transparent;\n color: inherit; font: inherit; font-size: 14px; line-height: 22px;\n padding: 4px 12px 0 14px;\n}\n.rm-composer-grow textarea:disabled { opacity: .55; cursor: not-allowed; }\n.rm-composer-row {\n display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;\n gap: 10px; min-width: 0; padding: 2px 8px 4px;\n}\n.rm-composer-tools, .rm-composer-modes, .rm-composer-trailing {\n display: flex; align-items: center; min-width: 0; gap: 8px;\n}\n.rm-composer-trailing { flex: none; margin-left: auto; }\n.rm-composer-add {\n width: 28px; height: 28px; border: none; border-radius: 999px;\n background: color-mix(in srgb, var(--rm-fg, #e8eaed) 8%, transparent);\n color: inherit; cursor: pointer; display: grid; place-items: center;\n}\n.rm-composer-add:hover, .rm-composer-add[aria-pressed="true"] {\n background: color-mix(in srgb, var(--rm-accent, #5b8cff) 22%, transparent);\n}\n.rm-composer-select {\n max-width: 220px; height: 28px; border: none; border-radius: 8px;\n background: transparent; color: var(--rm-quiet, #8b93a7);\n font: inherit; font-size: 13px; font-weight: 500; line-height: 20px;\n padding: 0 8px; cursor: pointer; white-space: nowrap;\n}\n.rm-composer-select:hover:not(:disabled) {\n background: color-mix(in srgb, var(--rm-fg, #e8eaed) 8%, transparent);\n}\n.rm-composer-select:disabled { opacity: .5; cursor: default; }\n.rm-composer-model-text { display: inline; }\n.rm-composer-model-icon { display: none; font-size: 10px; }\n.rm-composer-effort {\n margin-left: 6px; font-size: 11px; opacity: .75;\n text-transform: uppercase; letter-spacing: .02em;\n}\n.rm-composer-send {\n width: 34px; height: 34px; border: none; border-radius: 999px;\n background: var(--rm-accent, #5b8cff); color: #fff; cursor: pointer;\n display: grid; place-items: center; transform: translateY(-2px);\n}\n.rm-composer-send:hover:not(:disabled) { filter: brightness(1.08); }\n.rm-composer-send:disabled { opacity: .4; cursor: default; }\n.rm-composer-popwrap { position: relative; }\n.rm-composer-menu {\n position: absolute; bottom: calc(100% + 6px); left: 0; z-index: 40;\n min-width: 200px; max-height: 320px; overflow: auto;\n border: 1px solid var(--rm-border, #2a2f3a); border-radius: 12px;\n background: var(--rm-elev, #12151c); box-shadow: 0 8px 24px rgba(0,0,0,.28);\n padding: 6px; display: flex; flex-direction: column; gap: 2px;\n}\n.rm-composer-menu-wide { min-width: 280px; max-width: 340px; }\n.rm-composer-menu-label {\n font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em;\n color: var(--rm-quiet, #8b93a7); padding: 6px 8px 2px;\n}\n.rm-composer-menu button[role="menuitem"] {\n text-align: left; border: none; background: transparent; color: inherit;\n font: inherit; font-size: 13px; padding: 6px 8px; border-radius: 8px; cursor: pointer;\n}\n.rm-composer-menu button[role="menuitem"]:hover,\n.rm-composer-menu button[role="menuitem"].is-active {\n background: color-mix(in srgb, var(--rm-accent, #5b8cff) 16%, transparent);\n}\n.rm-composer-check {\n display: flex; align-items: center; gap: 8px; font-size: 13px; padding: 4px 8px; cursor: pointer;\n}\n.rm-grant-row {\n display: flex; align-items: center; justify-content: space-between; gap: 8px;\n padding: 2px 8px; font-size: 12.5px;\n}\n.rm-grant-row select {\n font: inherit; font-size: 12px; border-radius: 6px;\n border: 1px solid var(--rm-border, #2a2f3a); background: transparent; color: inherit; padding: 2px 6px;\n}\n.rm-peer-grant-block { border-top: 1px solid var(--rm-border, #2a2f3a); padding-top: 4px; margin-top: 4px; }\n.rm-peer-grant-name { font-size: 12px; font-weight: 600; padding: 4px 8px; overflow: hidden; text-overflow: ellipsis; }\n\n/* Hide legacy tight composer if still referenced */\n.rm-composer { display: none; }\n\n.rm-composer-grow { position: relative; }\n.rm-mention-menu {\n position: absolute; left: 8px; right: 8px; bottom: calc(100% + 6px); z-index: 50;\n max-height: 240px; overflow: auto;\n border: 1px solid var(--rm-border, #2a2f3a); border-radius: 12px;\n background: var(--rm-elev, #12151c); box-shadow: 0 10px 28px rgba(0,0,0,.32);\n padding: 4px; display: flex; flex-direction: column; gap: 2px;\n}\n.rm-mention-item {\n display: flex; align-items: center; gap: 8px; text-align: left;\n border: none; background: transparent; color: inherit; font: inherit;\n padding: 6px 8px; border-radius: 8px; cursor: pointer;\n}\n.rm-mention-item:hover, .rm-mention-item.is-active {\n background: color-mix(in srgb, var(--rm-accent, #5b8cff) 18%, transparent);\n}\n.rm-mention-item-body { display: flex; flex-direction: column; min-width: 0; flex: 1; }\n.rm-mention-item-name { font-size: 13px; font-weight: 600; }\n.rm-mention-item-meta { font-size: 11px; color: var(--rm-quiet, #8b93a7); }\n\n\n/* New Room shortcut under workspace (mirrors New Session affordance) */\n.rm-new-room {\n display: flex; align-items: center; justify-content: center; gap: 6px;\n width: 100%; height: 36px; margin-top: 8px;\n border: .5px solid var(--rm-border); border-radius: 10px;\n background: var(--dsw-alias-button-elevated-fill, var(--rm-panel));\n color: var(--rm-label); cursor: pointer; font-weight: 500; font-size: 13.5px;\n}\n.rm-new-room:hover { background: var(--dsw-alias-button-floating-hover, var(--rm-hover)); }\n.rm-new-room svg { flex: none; }\n\n/* Wide / landscape create + browse modals (overrides default 380px Modal card) */\n.rm-modal-wide {\n width: min(720px, calc(100vw - 48px)) !important;\n max-height: min(640px, calc(100vh - 48px));\n}\n.rm-form-wide {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px 18px;\n align-items: start;\n}\n.rm-form-wide .rm-span-2 { grid-column: 1 / -1; }\n.rm-browse { display: flex; flex-direction: column; gap: 12px; min-height: 280px; max-height: 420px; }\n.rm-browse-search { flex: none; }\n.rm-browse-status { font-size: 11.5px; color: var(--rm-quiet); white-space: nowrap; }\n.rm-browse-list { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 6px; }\n@media (max-width: 640px) {\n .rm-form-wide { grid-template-columns: 1fr; }\n .rm-modal-wide { width: min(420px, calc(100vw - 32px)) !important; }\n}\n\n\n/* \u2500\u2500 panel layout: rooms nav (grouped by native dsh workspace) + content \u2500\u2500 */\n.rm-main { flex-direction: row; }\n.rm-main-body { display: flex; flex-direction: column; flex: 1; min-width: 0; min-height: 0; }\n.rm-main-body > .rm-welcome-wrap { flex: 1; }\n.rm-main-body > .rm-workspace { flex: 1; }\n.rm-nav {\n flex: 0 0 248px; width: 248px; display: flex; flex-direction: column; gap: 8px; min-height: 0;\n padding: 10px 8px; border-right: .5px solid var(--rm-border); background: var(--rm-bg);\n}\n.rm-nav-head { display: flex; align-items: center; gap: 4px; padding: 0 4px; }\n.rm-nav-scroll { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 10px; }\n.rm-ws-group { display: flex; flex-direction: column; gap: 2px; }\n.rm-ws-head {\n display: flex; align-items: center; gap: 6px; height: 30px; padding: 0 2px 0 8px;\n font-size: 12.5px; font-weight: 600; color: var(--rm-muted);\n}\n.rm-ws-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-ws-add { width: 24px; height: 24px; }\n.rm-ws-empty { padding: 2px 10px 6px 30px; font-size: 11.5px; }\n.rm-nav .rm-room-item { padding: 6px 8px 6px 14px; }\n.rm-session-mark { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; color: var(--rm-accent, var(--dsw-alias-state-business-primary)); }\n\n/* \u2500\u2500 local multi-agent (0.3) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-dot-busy { background: var(--dsw-alias-state-warning-primary, #f0a020); }\n.rm-agent-tag { display: inline-flex; align-items: center; gap: 3px; margin-left: 6px; padding: 0 6px; border-radius: 999px; font-size: 10.5px; line-height: 17px; color: var(--rm-accent, #5b8cff); background: color-mix(in srgb, var(--rm-accent, #5b8cff) 12%, transparent); white-space: nowrap; }\n.rm-msg-id { margin-left: 6px; font-size: 11px; color: var(--rm-quiet); font-family: var(--dsw-font-mono, ui-monospace, monospace); }\n.rm-agent-row { display: flex; align-items: center; gap: 8px; padding: 6px 4px; border-radius: 8px; }\n.rm-agent-row:hover { background: var(--rm-hover); }\n.rm-agent-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }\n.rm-agent-name { font-size: 13px; font-weight: 600; color: var(--rm-label); display: flex; align-items: center; }\n.rm-agent-sub { font-size: 11px; color: var(--rm-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.rm-agent-row .rm-member-actions { display: inline-flex !important; opacity: 0.65; }\n.rm-agent-row:hover .rm-member-actions { opacity: 1; }\n.rm-num-row { display: flex; gap: 8px; margin: 4px 0 8px; }\n.rm-num-setting { flex: 1; display: flex; flex-direction: column; gap: 3px; font-size: 11px; color: var(--rm-muted); }\n.rm-num-setting input { width: 100%; box-sizing: border-box; padding: 5px 8px; border: 1px solid var(--rm-border); border-radius: 7px; background: var(--rm-bg); color: var(--rm-label); font: inherit; font-size: 13px; }\n.rm-runs { display: flex; flex-direction: column; gap: 4px; }\n.rm-run { display: grid; grid-template-columns: 22px auto 1fr auto; align-items: center; gap: 6px; padding: 5px 6px; border-radius: 7px; font-size: 12px; border: 1px solid var(--rm-border); }\n.rm-run-running { border-color: color-mix(in srgb, var(--dsw-alias-state-warning-primary, #f0a020) 55%, transparent); background: color-mix(in srgb, var(--dsw-alias-state-warning-primary, #f0a020) 8%, transparent); }\n.rm-run-queued, .rm-run-approval { border-style: dashed; }\n.rm-run-done, .rm-run-stopped, .rm-run-failed { opacity: 0.7; }\n.rm-run-pos { font-size: 11px; color: var(--rm-quiet); text-align: center; font-variant-numeric: tabular-nums; }\n.rm-run-who { font-weight: 600; color: var(--rm-label); white-space: nowrap; }\n.rm-run-what { color: var(--rm-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }\n.rm-stop-btn { display: inline-flex; align-items: center; gap: 5px; height: 26px; padding: 0 10px; border-radius: 999px; border: 1px solid color-mix(in srgb, var(--dsw-alias-state-danger-primary, #e5484d) 60%, transparent); background: color-mix(in srgb, var(--dsw-alias-state-danger-primary, #e5484d) 10%, transparent); color: var(--dsw-alias-state-danger-primary, #e5484d); font-size: 12px; cursor: pointer; white-space: nowrap; }\n.rm-stop-btn:hover { background: color-mix(in srgb, var(--dsw-alias-state-danger-primary, #e5484d) 18%, transparent); }\n.rm-badge-local { color: var(--dsw-alias-state-success-primary, #2bb673); }\n.rm-session-agent { color: var(--rm-accent, #5b8cff); }\n.rm-preset-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px; }\n.rm-preset { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; min-width: 150px; flex: 1; padding: 8px 10px; border: 1px solid var(--rm-border); border-radius: 10px; background: transparent; color: var(--rm-label); text-align: left; cursor: pointer; font: inherit; font-size: 12px; }\n.rm-preset span { color: var(--rm-muted); font-size: 11px; line-height: 1.35; }\n.rm-preset.is-on { border-color: var(--rm-accent, #5b8cff); background: color-mix(in srgb, var(--rm-accent, #5b8cff) 12%, transparent); }\n.rm-preset-check { flex-direction: row; align-items: center; gap: 8px; min-width: 120px; flex: 0 1 auto; }\n.rm-local-box { display: flex; flex-direction: column; gap: 8px; padding: 10px 12px; border: 1px dashed var(--rm-border); border-radius: 12px; }\n.rm-textarea { width: 100%; box-sizing: border-box; min-height: 84px; resize: vertical; font: inherit; font-size: 13px; padding: 8px 10px; }\n.rm-switch-col { display: flex; flex-direction: column; gap: 6px; }\n.rm-modal-xl { width: min(1180px, 96vw) !important; max-width: 96vw !important; }\n.rm-merge { display: grid; grid-template-columns: 260px 1fr; gap: 14px; min-height: 420px; }\n.rm-merge-list { display: flex; flex-direction: column; gap: 6px; border-right: 1px solid var(--rm-border); padding-right: 12px; }\n.rm-merge-agent { display: flex; align-items: center; gap: 8px; padding: 8px; border: 1px solid transparent; border-radius: 10px; background: transparent; color: var(--rm-label); cursor: pointer; text-align: left; font: inherit; }\n.rm-merge-agent:hover { background: var(--rm-hover); }\n.rm-merge-agent.is-on { border-color: var(--rm-accent, #5b8cff); background: color-mix(in srgb, var(--rm-accent, #5b8cff) 10%, transparent); }\n.rm-merge-agent-body { flex: 1; min-width: 0; display: flex; flex-direction: column; font-size: 12px; }\n.rm-merge-agent-body .rm-mono { font-size: 10.5px; color: var(--rm-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.rm-merge-main { min-width: 0; display: flex; flex-direction: column; gap: 8px; }\n.rm-merge-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; font-size: 13px; }\n.rm-merge-files { display: flex; flex-wrap: wrap; gap: 6px 14px; font-size: 12px; }\n.rm-confirm-text { font-size: 12px; color: var(--rm-label); margin-right: 4px; }\n.rm-diff { margin: 0; max-height: 52vh; overflow: auto; padding: 8px 0; border: 1px solid var(--rm-border); border-radius: 10px; background: var(--rm-panel); font-family: var(--dsw-font-mono, ui-monospace, monospace); font-size: 12px; line-height: 1.5; }\n.rm-diff > div { padding: 0 12px; white-space: pre; }\n.rm-diff-add { background: color-mix(in srgb, #2bb673 16%, transparent); }\n.rm-diff-del { background: color-mix(in srgb, #e5484d 16%, transparent); }\n.rm-diff-hunk { color: var(--rm-accent, #5b8cff); }\n.rm-diff-head, .rm-diff-file { color: var(--rm-muted); font-weight: 600; }\n.rm-diff-addn { color: #2bb673; }\n.rm-diff-deln { color: #e5484d; }\n.rm-ok { padding: 8px 10px; border-radius: 8px; font-size: 12.5px; color: var(--dsw-alias-state-success-primary, #2bb673); background: color-mix(in srgb, #2bb673 10%, transparent); }\n\n/* \u2500\u2500 create-room dialog (0.3.1): wide, scrollable, tabbed \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-modal-scrollable {\n width: min(920px, calc(100vw - 48px)) !important;\n max-width: 96vw !important;\n /* Bound height so the flex child (.rm-modal-scroll) can scroll instead of clipping. */\n height: min(720px, 100%) !important;\n max-height: 100% !important;\n}\n.rm-modal-create { width: min(960px, calc(100vw - 48px)) !important; }\n.rm-modal-scroll {\n flex: 1 1 auto;\n min-height: 0;\n overflow-x: hidden;\n overflow-y: auto;\n overscroll-behavior: contain;\n}\n.rm-create { display: flex; flex-direction: column; gap: 12px; }\n.rm-tabs {\n display: flex; gap: 4px; padding: 2px; border-radius: 10px;\n background: color-mix(in srgb, var(--rm-border) 55%, transparent);\n flex-wrap: wrap;\n}\n.rm-tab {\n flex: 1 1 auto; min-width: 88px; height: 32px; padding: 0 12px; border: 0; border-radius: 8px;\n background: transparent; color: var(--rm-muted); font: inherit; font-size: 13px; font-weight: 560;\n cursor: pointer;\n}\n.rm-tab.is-on { background: var(--rm-panel, var(--dsw-alias-bg-layer-2)); color: var(--rm-label); box-shadow: 0 0 0 1px var(--rm-border); }\n.rm-create-pane { min-height: 280px; }\n.rm-mode-wrap { flex-wrap: wrap; }\n.rm-preset-sub { display: block; color: var(--rm-muted); font-size: 11px; font-weight: 400; line-height: 1.35; margin-top: 1px; }\n.rm-create-custom {\n margin-top: 14px; padding: 12px; border: 1px dashed var(--rm-border); border-radius: 12px;\n display: flex; flex-direction: column; gap: 10px;\n}\n.rm-create-custom-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }\n.rm-draft-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }\n.rm-draft-item {\n display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: 8px;\n border: 1px solid var(--rm-border); background: var(--rm-panel);\n}\n.rm-draft-main { flex: 1; min-width: 0; display: flex; flex-direction: column; font-size: 12.5px; }\n.rm-draft-main span { color: var(--rm-muted); font-size: 11px; }\n.rm-draft-form { margin-top: 4px; padding-top: 10px; border-top: 1px dashed var(--rm-border); }\n.rm-linkish { background: none; border: 0; color: var(--rm-accent, #5b8cff); cursor: pointer; font: inherit; font-size: inherit; padding: 0; text-decoration: underline; }\n@media (max-width: 720px) {\n .rm-modal-create, .rm-modal-scrollable { width: min(100vw - 24px, 520px) !important; }\n}\n';
41
41
 
42
42
  // src/client/store.js
43
43
  var API = "/api/collab";
@@ -215,6 +215,37 @@ function createRoomsStore() {
215
215
  },
216
216
  async mcp(roomId, tool, args, target) {
217
217
  return post(`/rooms/${encodeURIComponent(roomId)}/mcp`, { tool, args, target });
218
+ },
219
+ // ── local multi-agent ──
220
+ async presets() {
221
+ return (await call("/presets")).presets;
222
+ },
223
+ async addAgent(roomId, body) {
224
+ return post(`/rooms/${encodeURIComponent(roomId)}/agents`, body);
225
+ },
226
+ async updateAgent(roomId, id, body) {
227
+ return post(`/rooms/${encodeURIComponent(roomId)}/agents/${encodeURIComponent(id)}`, body);
228
+ },
229
+ async removeAgent(roomId, id) {
230
+ return post(`/rooms/${encodeURIComponent(roomId)}/agents/${encodeURIComponent(id)}?op=delete`);
231
+ },
232
+ async saveLocal(roomId, patch) {
233
+ return post(`/rooms/${encodeURIComponent(roomId)}/local`, patch);
234
+ },
235
+ async setTransport(roomId, target, force) {
236
+ return post(`/rooms/${encodeURIComponent(roomId)}/transport`, { target, force: !!force });
237
+ },
238
+ async stop(roomId, agentId) {
239
+ return post(`/rooms/${encodeURIComponent(roomId)}/stop`, agentId ? { agentId } : {});
240
+ },
241
+ async diffs(roomId) {
242
+ return call(`/rooms/${encodeURIComponent(roomId)}/diffs`);
243
+ },
244
+ async merge(roomId, agentId) {
245
+ return post(`/rooms/${encodeURIComponent(roomId)}/merge`, { agentId, approve: true });
246
+ },
247
+ async discard(roomId, agentId) {
248
+ return post(`/rooms/${encodeURIComponent(roomId)}/discard`, { agentId, approve: true });
218
249
  }
219
250
  };
220
251
  const api = {
@@ -270,6 +301,17 @@ var LockIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { size: 1
270
301
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "3.5", y: "7", width: "9", height: "6.25", rx: "1.5" }),
271
302
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5.5 7V5.25a2.5 2.5 0 0 1 5 0V7" })
272
303
  ] });
304
+ var BotIcon = ({ size = 14 }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { size, children: [
305
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "3", y: "5", width: "10", height: "7.5", rx: "2" }),
306
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 2.5V5M6 8.5h.01M10 8.5h.01M6.25 10.75h3.5" })
307
+ ] });
308
+ var StopIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Svg, { size: 12, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1.5" }) });
309
+ var MergeIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { size: 14, children: [
310
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "5", cy: "3.75", r: "1.5" }),
311
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "5", cy: "12.25", r: "1.5" }),
312
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "11", cy: "8", r: "1.5" }),
313
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 5.25v5.5M5 5.5c0 2 1.5 2.5 4.5 2.5" })
314
+ ] });
273
315
  var PeopleIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { size: 14, children: [
274
316
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "6", cy: "5.5", r: "2.25" }),
275
317
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M2.5 13c.4-2 1.8-3.25 3.5-3.25S9.1 11 9.5 13" }),
@@ -390,6 +432,10 @@ function RoomsNav({ store }) {
390
432
  }
391
433
  function RoomSessionMark({ store, sessionId }) {
392
434
  const s = useStore(store);
435
+ for (const r of s.rooms || []) {
436
+ const ag = (r.local && r.local.agents || []).find((x) => x.sessionId === sessionId);
437
+ if (ag) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-session-mark rm-session-agent", title: `DSH Room \xB7 ${r.name} \xB7 ${ag.name} (${ag.agentId})`, "aria-label": `DSH Room ${r.name} agent ${ag.name}`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BotIcon, { size: 13 }) });
438
+ }
393
439
  const room = (s.rooms || []).find((r) => r.sessionId === sessionId);
394
440
  if (!room) return null;
395
441
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-session-mark", title: `DSH Room \xB7 ${room.name}`, "aria-label": `DSH Room ${room.name}`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomsIcon, { size: 13 }) });
@@ -415,14 +461,14 @@ function renderMentions(text) {
415
461
  function PanelIcon({ size }) {
416
462
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomsIcon, { size });
417
463
  }
418
- function Message({ m, showAuthor }) {
464
+ function Message({ m, showAuthor, who }) {
419
465
  if (m.kind === "task.create") {
420
466
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-msg rm-msg-event", "data-message-id": m.id, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-task-card", children: [
421
467
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-task-head", children: [
422
468
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TaskIcon, {}),
423
469
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
424
470
  "Task \xB7 ",
425
- m.from
471
+ who && who.name || m.from
426
472
  ] }),
427
473
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "info", children: "open" }),
428
474
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-msg-time", children: fmtTime(m.ts) })
@@ -435,7 +481,7 @@ function Message({ m, showAuthor }) {
435
481
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-msg rm-msg-event", "data-message-id": m.id, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-task-head", children: [
436
482
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TaskIcon, {}),
437
483
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
438
- m.self ? "You" : m.from,
484
+ m.self ? "You" : who && who.name || m.from,
439
485
  " ",
440
486
  m.kind === "task.claim" ? "claimed" : "updated",
441
487
  " task"
@@ -455,14 +501,20 @@ function Message({ m, showAuthor }) {
455
501
  showAuthor ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: m.from, size: 28 }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {}),
456
502
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
457
503
  showAuthor ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-msg-head", children: [
458
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-msg-author", children: m.from }),
504
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-msg-author", children: who && who.name ? who.name : m.from }),
505
+ who && who.name ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-msg-id", children: m.from }) : null,
506
+ who && who.local ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-tag", children: [
507
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BotIcon, { size: 11 }),
508
+ who.moderator ? "moderator" : "local agent"
509
+ ] }) : null,
459
510
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-msg-time", children: fmtTime(m.ts) })
460
511
  ] }) : null,
461
512
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-msg-body", children: renderMentions(m.text) })
462
513
  ] })
463
514
  ] });
464
515
  }
465
- function ChatBody({ messages }) {
516
+ function ChatBody({ messages, members }) {
517
+ const byId = (0, import_react.useMemo)(() => Object.fromEntries((members || []).map((x) => [x.agentId, x])), [members]);
466
518
  const ref = (0, import_react.useRef)(null);
467
519
  (0, import_react.useEffect)(() => {
468
520
  const el = ref.current;
@@ -478,7 +530,7 @@ function ChatBody({ messages }) {
478
530
  prev = null;
479
531
  }
480
532
  const showAuthor = !prev || prev.from !== m.from || m.ts - prev.ts > 5 * 6e4;
481
- nodes.push(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Message, { m, showAuthor }, m.id));
533
+ nodes.push(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Message, { m, showAuthor, who: byId[m.from] }, m.id));
482
534
  prev = m;
483
535
  }
484
536
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-messages", ref, children: nodes.length ? nodes : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-chat-hint", children: "No messages yet. Send a message, @mention an agent to wake it, or create a task from the composer." }) });
@@ -505,7 +557,7 @@ function MentionMenu({ items, active, onPick, onHover }) {
505
557
  onPick(it);
506
558
  },
507
559
  children: [
508
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: it.id === "all" ? "all" : it.id, size: 22 }),
560
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: it.id === "all" ? "all" : it.name || it.id, size: 22 }),
509
561
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-mention-item-body", children: [
510
562
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-mention-item-name", children: [
511
563
  "@",
@@ -513,6 +565,10 @@ function MentionMenu({ items, active, onPick, onHover }) {
513
565
  ] }),
514
566
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-mention-item-meta", children: it.hint })
515
567
  ] }),
568
+ it.local ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-tag", children: [
569
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BotIcon, { size: 11 }),
570
+ "local"
571
+ ] }) : null,
516
572
  it.online === false ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "quiet", children: "offline" }) : null
517
573
  ]
518
574
  },
@@ -537,14 +593,20 @@ function Composer({ room, store, draft, setDraft }) {
537
593
  if (!mq) return [];
538
594
  const q = mq.query.toLowerCase();
539
595
  const members = (room.members || []).filter((m) => !m.self);
540
- const known = members.map((m) => ({
541
- id: m.agentId,
542
- hint: m.online === false ? "member \xB7 offline" : "member \xB7 wake",
543
- online: m.online !== false
544
- }));
545
- const all = { id: "all", hint: "Everyone \xB7 wake all members", online: true };
596
+ const localAgents = room.local && room.local.agents || [];
597
+ const seen = /* @__PURE__ */ new Set();
598
+ const known = [];
599
+ for (const la of localAgents) {
600
+ seen.add(la.agentId);
601
+ known.push({ id: la.agentId, name: la.name, local: true, hint: `${la.name} \xB7 local agent${la.moderator ? " \xB7 moderator" : ""}${la.state && la.state !== "idle" ? ` \xB7 ${la.state}` : ""}`, online: la.online !== false });
602
+ }
603
+ for (const m of members) {
604
+ if (seen.has(m.agentId)) continue;
605
+ known.push({ id: m.agentId, name: m.name, hint: `${m.name ? m.name + " \xB7 " : ""}${m.hostedBy ? "remote agent" : "member"}${m.online === false ? " \xB7 offline" : " \xB7 wake"}`, online: m.online !== false });
606
+ }
607
+ const all = { id: "all", hint: localAgents.length ? "Everyone \xB7 wakes all local agents in parallel" : "Everyone \xB7 wake all members", online: true };
546
608
  const list = [all, ...known];
547
- return list.filter((it) => !q || it.id.toLowerCase().startsWith(q) || ("@" + it.id).toLowerCase().startsWith("@" + q));
609
+ return list.filter((it) => !q || it.id.toLowerCase().startsWith(q) || it.name && it.name.toLowerCase().startsWith(q));
548
610
  })();
549
611
  const mc = room.memberConfig || {};
550
612
  const modelLabel = mc.model || "Default model";
@@ -582,6 +644,23 @@ function Composer({ room, store, draft, setDraft }) {
582
644
  a.toast(e.message);
583
645
  }
584
646
  };
647
+ const syncCaret = (el) => {
648
+ if (el) setCaret(el.selectionStart == null ? el.value.length : el.selectionStart);
649
+ };
650
+ const insertMention = (it) => {
651
+ if (!mq || !it) return;
652
+ const next = draft.slice(0, mq.start) + "@" + it.id + " " + draft.slice(mq.end);
653
+ const pos = mq.start + it.id.length + 2;
654
+ setDraft(next);
655
+ setCaret(pos);
656
+ requestAnimationFrame(() => {
657
+ const el = taRef.current;
658
+ if (el) {
659
+ el.focus();
660
+ el.setSelectionRange(pos, pos);
661
+ }
662
+ });
663
+ };
585
664
  const submit = async () => {
586
665
  const text = draft.trim();
587
666
  if (!text || busy) return;
@@ -778,7 +857,15 @@ function RoomDetail({ room, messages, store, mention, assign }) {
778
857
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: m.agentId, size: 26 }),
779
858
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-dot rm-dot-" + (m.online === false ? "offline" : "online"), style: { position: "absolute", right: -1, bottom: -1, border: "2px solid var(--rm-bg)" } })
780
859
  ] }),
781
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-member-name", children: m.agentId }),
860
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-member-name", children: m.name ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
861
+ m.name,
862
+ " ",
863
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-msg-id", children: m.agentId })
864
+ ] }) : m.agentId }),
865
+ m.local ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-tag", children: [
866
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BotIcon, { size: 11 }),
867
+ m.moderator ? "moderator" : "local"
868
+ ] }) : m.hostedBy && !m.self ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-agent-tag", children: "remote agent" }) : null,
782
869
  m.self ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "quiet", children: "you" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-member-actions", children: [
783
870
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mini", title: `@mention ${m.agentId}`, onClick: () => mention(m.agentId), children: "@" }),
784
871
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mini", title: `Assign a task to ${m.agentId}`, onClick: () => assign(m.agentId), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TaskIcon, {}) })
@@ -786,6 +873,9 @@ function RoomDetail({ room, messages, store, mention, assign }) {
786
873
  ] }, m.agentId)),
787
874
  !room.members.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: room.status === "online" ? "Only you are here." : "Offline." }) : null
788
875
  ] }),
876
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LocalAgentsSection, { room, store, mention }),
877
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OrchestrationSection, { room, store }),
878
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IsolationSection, { room, store }),
789
879
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", children: [
790
880
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-h", children: [
791
881
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TaskIcon, {}),
@@ -808,8 +898,10 @@ function RoomDetail({ room, messages, store, mention, assign }) {
808
898
  "Invite"
809
899
  ] }),
810
900
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: room.public ? "Public: listed in the relay directory with its key. Anyone can find, join, and read this room." : "Private: only agents holding the invite can join. The invite contains the room key \u2014 share it only with agents you trust." }),
901
+ room.transport === "local" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "Local-only room: switch Transport to Relay or LAN before inviting remote hosts." }) : null,
811
902
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-actions", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", onClick: copyInvite, icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LinkIcon, {}), children: "Copy invite" }) })
812
903
  ] }),
904
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TransportSection, { room, store }),
813
905
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", children: [
814
906
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-detail-h", children: "About" }),
815
907
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("dl", { className: "rm-about", children: [
@@ -822,7 +914,7 @@ function RoomDetail({ room, messages, store, mention, assign }) {
822
914
  " Private"
823
915
  ] }) }),
824
916
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", { children: "Transport" }),
825
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: room.transport === "lan" ? "LAN / VPS (Node relay)" : "Relay (Cloudflare)" }),
917
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: room.transport === "local" ? "Local only (in-process loopback)" : room.transport === "lan" ? "LAN / VPS (Node relay)" : "Relay (Cloudflare)" }),
826
918
  room.memberConfig?.workspaceLabel ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
827
919
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", { children: "Workspace" }),
828
920
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: room.memberConfig.workspaceLabel })
@@ -849,6 +941,391 @@ function RoomDetail({ room, messages, store, mention, assign }) {
849
941
  ] })
850
942
  ] });
851
943
  }
944
+ var RUN_TONE = { running: "warning", queued: "info", approval: "info", done: "success", failed: "danger", stopped: "quiet" };
945
+ var MODEL_CHOICES = ["", "deepseek-chat", "deepseek-reasoner"];
946
+ function LocalAgentsSection({ room, store, mention }) {
947
+ const a = store.actions;
948
+ const local = room.local || { agents: [] };
949
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", "data-rm-local-agents": "", children: [
950
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-h", children: [
951
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BotIcon, {}),
952
+ "Local agents \xB7 ",
953
+ local.agents.length
954
+ ] }),
955
+ local.agents.map((ag) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-agent-row", "data-agent-id": ag.agentId, children: [
956
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-avatar-wrap", children: [
957
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: ag.name, size: 26 }),
958
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-dot rm-dot-" + (ag.state === "running" ? "busy" : ag.online ? "online" : "offline"), style: { position: "absolute", right: -1, bottom: -1, border: "2px solid var(--rm-bg)" } })
959
+ ] }),
960
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-main", children: [
961
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-name", children: [
962
+ ag.name,
963
+ ag.moderator ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-agent-tag", children: "moderator" }) : null
964
+ ] }),
965
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-sub rm-mono", children: [
966
+ "@",
967
+ ag.agentId
968
+ ] }),
969
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-sub", children: [
970
+ ag.scope === "write" ? ag.isolation === "worktree" ? "writes \xB7 own worktree" : ag.isolation === "locks" ? "writes \xB7 file locks" : "writes" : "read-only",
971
+ ag.approvalRequired ? " \xB7 approval" : "",
972
+ ag.model ? ` \xB7 ${ag.model}` : "",
973
+ ag.reasoningEffort ? ` \xB7 ${ag.reasoningEffort}` : ""
974
+ ] })
975
+ ] }),
976
+ ag.state !== "idle" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: RUN_TONE[ag.state] || "neutral", children: ag.state }) : null,
977
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-member-actions", children: [
978
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mini", title: `@mention ${ag.name}`, onClick: () => mention(ag.agentId), children: "@" }),
979
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mini", title: `Edit ${ag.name}`, "aria-label": `Edit ${ag.name}`, onClick: () => a.openDialog({ kind: "agent", roomId: room.roomId, agentId: ag.agentId }), children: "\u270E" })
980
+ ] })
981
+ ] }, ag.agentId)),
982
+ !local.agents.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "Add a custom agent (free-form name, prompt, model, scope) or start from a preset. Each gets its own identity, session and scope." }) : null,
983
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-actions", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {}), onClick: () => a.openDialog({ kind: "agent", roomId: room.roomId }), children: "Add agent" }) })
984
+ ] });
985
+ }
986
+ function NumberSetting({ label, value, min, max, onSave, hint }) {
987
+ const [v, setV] = (0, import_react.useState)(String(value));
988
+ (0, import_react.useEffect)(() => setV(String(value)), [value]);
989
+ const commit = () => {
990
+ const n = Number(v);
991
+ if (Number.isFinite(n) && n !== value) onSave(Math.min(max, Math.max(min, Math.round(n))));
992
+ else setV(String(value));
993
+ };
994
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "rm-num-setting", title: hint, children: [
995
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: label }),
996
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "number", min, max, value: v, "aria-label": label, onChange: (e) => setV(e.target.value), onBlur: commit, onKeyDown: (e) => e.key === "Enter" && e.target.blur() })
997
+ ] });
998
+ }
999
+ function RunQueue({ room, store, compact }) {
1000
+ const a = store.actions;
1001
+ const runs = room.runs || [];
1002
+ const nameOf = (id) => (room.local && room.local.agents || []).find((x) => x.agentId === id)?.name || id;
1003
+ const active = runs.filter((r) => r.state === "running" || r.state === "queued" || r.state === "approval");
1004
+ const recent = runs.filter((r) => !active.includes(r)).slice(-3).reverse();
1005
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-runs", "data-rm-runs": "", children: [
1006
+ active.map((r, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-run rm-run-" + r.state, children: [
1007
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-run-pos", children: r.state === "running" ? "\u25B6" : r.state === "approval" ? "?" : `#${i + 1 - active.filter((x) => x.state === "running").length}` }),
1008
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-run-who", children: nameOf(r.agentId) }),
1009
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-run-what", title: r.summary, children: r.summary }),
1010
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: RUN_TONE[r.state], children: r.state === "approval" ? "needs approval" : r.state })
1011
+ ] }, r.id)),
1012
+ !compact ? recent.map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-run rm-run-" + r.state, children: [
1013
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-run-pos", children: "\u2713" }),
1014
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-run-who", children: nameOf(r.agentId) }),
1015
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-run-what", title: r.error || r.summary, children: r.error || r.summary }),
1016
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: RUN_TONE[r.state], children: r.state })
1017
+ ] }, r.id)) : null,
1018
+ !active.length && !recent.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "No agent runs yet." }) : null,
1019
+ active.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-actions", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", className: "rm-danger", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StopIcon, {}), onClick: () => a.stop(room.roomId).then((r) => a.toast(`Stopped ${r.stopped} run(s)`)).catch((e) => a.toast(e.message)), children: "Stop all agents" }) }) : null
1020
+ ] });
1021
+ }
1022
+ function OrchestrationSection({ room, store }) {
1023
+ const a = store.actions;
1024
+ const local = room.local;
1025
+ if (!local || !local.agents.length) return null;
1026
+ const save = (patch) => a.saveLocal(room.roomId, patch).catch((e) => a.toast(e.message));
1027
+ const active = (room.runs || []).filter((r) => r.state === "running" || r.state === "queued" || r.state === "approval");
1028
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", "data-rm-orchestration": "", children: [
1029
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-h", children: [
1030
+ "Runs \xB7 ",
1031
+ active.filter((r) => r.state === "running").length,
1032
+ "/",
1033
+ local.concurrency,
1034
+ " running",
1035
+ active.length > local.concurrency ? ` \xB7 ${active.length - Math.min(local.concurrency, active.filter((r) => r.state === "running").length)} queued` : ""
1036
+ ] }),
1037
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-num-row", children: [
1038
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NumberSetting, { label: "Parallel runs", value: local.concurrency, min: 1, max: 16, onSave: (n) => save({ concurrency: n }), hint: "Max local agent runs at once; the rest wait in the queue." }),
1039
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NumberSetting, { label: "Turn limit", value: local.turnLimit, min: 1, max: 100, onSave: (n) => save({ turnLimit: n }), hint: "Max agent-to-agent hand-offs per task/thread. A human message resets it." })
1040
+ ] }),
1041
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RunQueue, { room, store })
1042
+ ] });
1043
+ }
1044
+ function IsolationSection({ room, store }) {
1045
+ const a = store.actions;
1046
+ const local = room.local;
1047
+ if (!local || !local.agents.some((x) => x.scope === "write")) return null;
1048
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", "data-rm-isolation": "", children: [
1049
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-h", children: [
1050
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MergeIcon, {}),
1051
+ "Workspace isolation"
1052
+ ] }),
1053
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { className: "rm-input", "aria-label": "Isolation mode", value: local.isolation, onChange: (e) => a.saveLocal(room.roomId, { isolation: e.target.value }).catch((x) => a.toast(x.message)), children: [
1054
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "auto", children: "Auto \u2014 git worktree per writing agent, else file locks" }),
1055
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "worktree", children: "Git worktree per agent" }),
1056
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "locks", children: "File locks (shared workspace)" }),
1057
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "off", children: "Off (shared, no locks)" })
1058
+ ] }),
1059
+ local.locks && local.locks.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-note", children: [
1060
+ local.locks.length,
1061
+ " file lock(s): ",
1062
+ local.locks.slice(0, 4).map((l) => `${l.path} (${l.owner})`).join(", ")
1063
+ ] }) : null,
1064
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-actions", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MergeIcon, {}), onClick: () => a.openDialog({ kind: "merge", roomId: room.roomId }), children: "Review & merge" }) })
1065
+ ] });
1066
+ }
1067
+ function TransportSection({ room, store }) {
1068
+ const a = store.actions;
1069
+ const [busy, setBusy] = (0, import_react.useState)(null);
1070
+ const cur = room.transport === "local" ? "local" : room.transport === "lan" ? "relay" : "relay";
1071
+ const go = async (target) => {
1072
+ setBusy(target);
1073
+ try {
1074
+ await a.setTransport(room.roomId, target);
1075
+ a.toast(target === "local" ? "Local only \u2014 in-process, no network" : target === "lan" ? "Now on this machine's LAN relay \u2014 invite remote hosts" : "Now on the relay \u2014 invite remote hosts");
1076
+ } catch (e) {
1077
+ a.toast(e.message);
1078
+ } finally {
1079
+ setBusy(null);
1080
+ }
1081
+ };
1082
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", "data-rm-transport": "", children: [
1083
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-detail-h", children: "Transport" }),
1084
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-mode-row", children: [
1085
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", disabled: !!busy || room.public, className: "rm-mode-btn" + (cur === "local" ? " is-on" : ""), onClick: () => go("local"), title: room.public ? "Public rooms need a relay" : "In-process loopback: no network", children: busy === "local" ? "\u2026" : "Local only" }),
1086
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", disabled: !!busy, className: "rm-mode-btn" + (cur === "relay" && room.transport !== "local" ? " is-on" : ""), onClick: () => go("relay"), children: busy === "relay" ? "\u2026" : "Relay (VPS)" }),
1087
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", disabled: !!busy, className: "rm-mode-btn", onClick: () => go("lan"), title: "Embedded LAN relay on this machine (mDNS)", children: busy === "lan" ? "\u2026" : "LAN" })
1088
+ ] }),
1089
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: room.transport === "local" ? "All members are on this dsh \u2014 nothing leaves the process. Switch to Relay or LAN to invite remote hosts; local agents stay members." : "Local agents connect as separate members over this transport, alongside remote hosts." })
1090
+ ] });
1091
+ }
1092
+ function AgentDialog({ store, d }) {
1093
+ const a = store.actions;
1094
+ const s = useStore(store);
1095
+ const room = (s.rooms || []).find((r) => r.roomId === d.roomId);
1096
+ const existing = room && room.local ? room.local.agents.find((x) => x.agentId === d.agentId) : null;
1097
+ const [presets, setPresets] = (0, import_react.useState)([]);
1098
+ const blank = () => ({ preset: "", name: "", persona: "", systemPrompt: "", model: "", reasoningEffort: "medium", scope: "read", approvalRequired: false, moderator: false });
1099
+ const [f, setF] = (0, import_react.useState)(() => existing ? { preset: "", name: existing.name, persona: existing.persona || "", systemPrompt: existing.systemPrompt || "", model: existing.model || "", reasoningEffort: existing.reasoningEffort || "medium", scope: existing.scope, approvalRequired: !!existing.approvalRequired, moderator: !!existing.moderator } : blank());
1100
+ const [err, setErr] = (0, import_react.useState)(null);
1101
+ const [busy, setBusy] = (0, import_react.useState)(false);
1102
+ (0, import_react.useEffect)(() => {
1103
+ a.presets().then(setPresets).catch(() => {
1104
+ });
1105
+ }, []);
1106
+ const pick = (p) => setF({ ...f, preset: p.key, name: p.name, persona: p.persona || "", systemPrompt: p.systemPrompt || "", scope: p.scope, moderator: !!p.moderator, reasoningEffort: p.reasoningEffort || "medium" });
1107
+ const clearPreset = () => setF({ ...f, preset: "" });
1108
+ const submit = async () => {
1109
+ if (!f.name.trim()) {
1110
+ setErr("Name required");
1111
+ return;
1112
+ }
1113
+ setBusy(true);
1114
+ setErr(null);
1115
+ try {
1116
+ const body = { name: f.name.trim(), persona: f.persona, systemPrompt: f.systemPrompt, model: f.model || void 0, reasoningEffort: f.reasoningEffort, scope: f.scope, approvalRequired: f.approvalRequired, moderator: f.moderator };
1117
+ if (existing) await a.updateAgent(d.roomId, existing.agentId, body);
1118
+ else {
1119
+ const r = await a.addAgent(d.roomId, f.preset ? { ...body, preset: f.preset } : body);
1120
+ a.toast(`${r.name} joined as @${r.agentId}`);
1121
+ }
1122
+ a.closeDialog();
1123
+ } catch (e) {
1124
+ setErr(e.message);
1125
+ setBusy(false);
1126
+ }
1127
+ };
1128
+ const remove = async () => {
1129
+ setBusy(true);
1130
+ try {
1131
+ const r = await a.removeAgent(d.roomId, existing.agentId);
1132
+ a.toast(`Removed ${existing.name}${r.branchKept ? ` \xB7 branch ${r.branchKept} kept` : ""}`);
1133
+ a.closeDialog();
1134
+ } catch (e) {
1135
+ setErr(e.message);
1136
+ setBusy(false);
1137
+ }
1138
+ };
1139
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1140
+ import_dsh_client_ui_primitives.Modal,
1141
+ {
1142
+ open: true,
1143
+ onClose: a.closeDialog,
1144
+ title: existing ? `Edit ${existing.name}` : "Add local agent",
1145
+ closeLabel: "Close",
1146
+ className: "rm-modal-wide rm-modal-scrollable",
1147
+ contentClassName: "rm-modal-scroll",
1148
+ description: existing ? `Member @${existing.agentId} \xB7 own identity and dsh session in this room's workspace.` : "Free-form agent: own name, persona, prompt, model, scope. Presets only fill the form \u2014 you can edit every field. Remote peers see it as a separate member.",
1149
+ footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1150
+ existing ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", className: "rm-danger", disabled: busy, onClick: remove, children: "Remove" }) : null,
1151
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { flex: 1 } }),
1152
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Cancel" }),
1153
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", disabled: busy, onClick: submit, children: busy ? "Saving\u2026" : existing ? "Save" : "Add agent" })
1154
+ ] }),
1155
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-form rm-form-wide", "data-rm-agent-form": "", children: [
1156
+ !existing ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-span-2", children: [
1157
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Start from" }),
1158
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "Presets fill the fields below; switch to Custom anytime and edit freely." }),
1159
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-preset-row", children: [
1160
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-preset" + (!f.preset ? " is-on" : ""), onClick: () => setF(blank()), children: [
1161
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "Custom" }),
1162
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Blank form \u2014 your name, prompt, model" })
1163
+ ] }),
1164
+ presets.map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-preset" + (f.preset === p.key ? " is-on" : ""), onClick: () => pick(p), children: [
1165
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: p.name }),
1166
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: p.persona })
1167
+ ] }, p.key))
1168
+ ] })
1169
+ ] }) : null,
1170
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Name", htmlFor: "rm-ag-name", hint: "Member id is derived from it (e.g. coder.ab12).", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-ag-name", value: f.name, maxLength: 40, onChange: (e) => {
1171
+ const name2 = e.target.value;
1172
+ const presetName = (presets.find((p) => p.key === f.preset) || {}).name;
1173
+ setF({ ...f, name: name2, preset: f.preset && name2 !== presetName ? "" : f.preset });
1174
+ }, placeholder: "e.g. Data wrangler" }) }),
1175
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Persona", htmlFor: "rm-ag-persona", hint: "One line other members see.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-ag-persona", value: f.persona, maxLength: 200, onChange: (e) => setF({ ...f, persona: e.target.value, ...f.preset ? { preset: "" } : {} }), placeholder: "e.g. Cleans CSVs and writes summaries" }) }),
1176
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "System prompt", htmlFor: "rm-ag-sp", hint: "Prepended to every wake of this agent.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("textarea", { id: "rm-ag-sp", className: "rm-input rm-textarea", rows: 4, value: f.systemPrompt, maxLength: 4e3, onChange: (e) => setF({ ...f, systemPrompt: e.target.value, ...f.preset ? { preset: "" } : {} }), placeholder: "You are \u2026 Focus on \u2026 Report back with \u2026" }) }) }),
1177
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Model", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { className: "rm-input", "aria-label": "Model", value: f.model, onChange: (e) => setF({ ...f, model: e.target.value }), children: [
1178
+ MODEL_CHOICES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: m, children: m || "dsh default model" }, m)),
1179
+ f.model && !MODEL_CHOICES.includes(f.model) ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: f.model, children: f.model }) : null
1180
+ ] }) }),
1181
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Reasoning effort", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { className: "rm-input", "aria-label": "Reasoning effort", value: f.reasoningEffort, onChange: (e) => setF({ ...f, reasoningEffort: e.target.value }), children: ["low", "medium", "high", "max"].map((x) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: x, children: x }, x)) }) }),
1182
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Workspace scope", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-mode-row", children: [
1183
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (f.scope === "read" ? " is-on" : ""), onClick: () => setF({ ...f, scope: "read" }), children: "Read-only" }),
1184
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (f.scope === "write" ? " is-on" : ""), onClick: () => setF({ ...f, scope: "write" }), children: "Read + write" })
1185
+ ] }) }),
1186
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-col", children: [
1187
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-row", children: [
1188
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1189
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Approval required" }),
1190
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "Each run waits for your OK." })
1191
+ ] }),
1192
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: f.approvalRequired, onChange: (v) => setF({ ...f, approvalRequired: v }), label: "Approval required" })
1193
+ ] }),
1194
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-row", children: [
1195
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1196
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Moderator" }),
1197
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "Splits requests into tasks and summarizes. One per room." })
1198
+ ] }),
1199
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: f.moderator, onChange: (v) => setF({ ...f, moderator: v }), label: "Moderator" })
1200
+ ] })
1201
+ ] }),
1202
+ f.scope === "write" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2 rm-note", children: "Writes go to its own git worktree (branch room/<room>/<agent>) when the workspace is a git repo; you merge from \u201CReview & merge\u201D. Non-git workspaces use file locks." }) : null,
1203
+ f.preset ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-span-2 rm-note", children: [
1204
+ "Filled from the ",
1205
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: f.preset }),
1206
+ " preset \u2014 editing any field switches to Custom. ",
1207
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-linkish", onClick: clearPreset, children: "Clear preset tag" })
1208
+ ] }) : null,
1209
+ err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2 rm-error", children: err }) : null
1210
+ ] })
1211
+ }
1212
+ );
1213
+ }
1214
+ function DiffView({ patch }) {
1215
+ if (!patch) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "No changes." });
1216
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { className: "rm-diff", "data-rm-diff": "", children: patch.split("\n").map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: l.startsWith("+++") || l.startsWith("---") ? "rm-diff-file" : l.startsWith("+") ? "rm-diff-add" : l.startsWith("-") ? "rm-diff-del" : l.startsWith("@@") ? "rm-diff-hunk" : l.startsWith("diff ") ? "rm-diff-head" : "", children: l || " " }, i)) });
1217
+ }
1218
+ function MergeDialog({ store, d }) {
1219
+ const a = store.actions;
1220
+ const [data, setData] = (0, import_react.useState)(null);
1221
+ const [sel, setSel] = (0, import_react.useState)(null);
1222
+ const [confirm, setConfirm] = (0, import_react.useState)(null);
1223
+ const [busy, setBusy] = (0, import_react.useState)(false);
1224
+ const [result, setResult] = (0, import_react.useState)(null);
1225
+ const load = async () => {
1226
+ try {
1227
+ const r = await a.diffs(d.roomId);
1228
+ setData(r);
1229
+ setSel((cur) => cur && r.agents.some((x) => x.agentId === cur) ? cur : (r.agents.find((x) => x.ahead) || r.agents[0] || {}).agentId || null);
1230
+ } catch (e) {
1231
+ setData({ error: e.message, agents: [] });
1232
+ }
1233
+ };
1234
+ (0, import_react.useEffect)(() => {
1235
+ void load();
1236
+ }, [d.roomId]);
1237
+ const ag = data && data.agents.find((x) => x.agentId === sel);
1238
+ const run = async (kind) => {
1239
+ setBusy(true);
1240
+ setResult(null);
1241
+ try {
1242
+ const r = kind === "merge" ? await a.merge(d.roomId, sel) : await a.discard(d.roomId, sel);
1243
+ setResult(kind === "merge" ? r.merged ? { ok: true, text: `Merged into the main workspace (${r.commit}).` } : { ok: false, text: `${r.error || "Not merged"}${r.conflicts && r.conflicts.length ? ": " + r.conflicts.join(", ") : ""}` } : { ok: true, text: r.discarded ? `Discarded ${r.branch}.` : "Nothing to discard." });
1244
+ await load();
1245
+ } catch (e) {
1246
+ setResult({ ok: false, text: e.message });
1247
+ } finally {
1248
+ setBusy(false);
1249
+ setConfirm(null);
1250
+ }
1251
+ };
1252
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1253
+ import_dsh_client_ui_primitives.Modal,
1254
+ {
1255
+ open: true,
1256
+ onClose: a.closeDialog,
1257
+ title: "Review & merge agent changes",
1258
+ closeLabel: "Close",
1259
+ className: "rm-modal-wide rm-modal-xl",
1260
+ description: "Each writing agent works on its own branch. Review the diff, then merge into (or discard from) your main workspace. Nothing is merged without your confirmation.",
1261
+ footer: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Close" }),
1262
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-root rm-merge", "data-rm-merge": "", children: !data ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "Loading diffs\u2026" }) : data.error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-error", children: data.error }) : !data.agents.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "No writing agents in this room." }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1263
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-merge-list", children: data.agents.map((x) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-merge-agent" + (x.agentId === sel ? " is-on" : ""), onClick: () => {
1264
+ setSel(x.agentId);
1265
+ setConfirm(null);
1266
+ setResult(null);
1267
+ }, children: [
1268
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: x.name, size: 24 }),
1269
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-merge-agent-body", children: [
1270
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: x.name }),
1271
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-mono", children: x.branch || (x.isolation === "locks" ? "shared workspace \xB7 locks" : "no worktree yet") })
1272
+ ] }),
1273
+ x.conflicts && x.conflicts.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "danger", children: "conflict" }) : x.ahead ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_dsh_client_ui_primitives.Tag, { tone: "info", children: [
1274
+ x.files.length,
1275
+ " file",
1276
+ x.files.length === 1 ? "" : "s"
1277
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "quiet", children: "clean" })
1278
+ ] }, x.agentId)) }),
1279
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-merge-main", children: ag ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1280
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-merge-head", children: [
1281
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1282
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: ag.name }),
1283
+ " ",
1284
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-mono", children: ag.branch || "" }),
1285
+ ag.ahead ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-note", style: { margin: "0 0 0 8px" }, children: [
1286
+ ag.ahead,
1287
+ " commit(s) ahead"
1288
+ ] }) : null
1289
+ ] }),
1290
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-actions", style: { margin: 0 }, children: confirm ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1291
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-confirm-text", children: confirm === "merge" ? `Merge ${ag.files.length} file(s) into the main workspace?` : `Discard ${ag.name}'s branch and worktree?` }),
1292
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", onClick: () => setConfirm(null), children: "Cancel" }),
1293
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "primary", disabled: busy, onClick: () => run(confirm), children: busy ? "Working\u2026" : confirm === "merge" ? "Confirm merge" : "Confirm discard" })
1294
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1295
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "ghost", className: "rm-danger", disabled: !ag.branch || busy, onClick: () => setConfirm("discard"), children: "Discard" }),
1296
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "primary", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MergeIcon, {}), disabled: !ag.ahead || busy, onClick: () => setConfirm("merge"), children: "Merge into main" })
1297
+ ] }) })
1298
+ ] }),
1299
+ result ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: result.ok ? "rm-ok" : "rm-error", children: result.text }) : null,
1300
+ ag.conflicts && ag.conflicts.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-error", children: [
1301
+ "Would conflict with the main workspace: ",
1302
+ ag.conflicts.join(", "),
1303
+ " \u2014 resolve on the branch or discard."
1304
+ ] }) : null,
1305
+ ag.mainDirty && ag.mainDirty.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-error", children: [
1306
+ "Uncommitted changes in the main workspace touch: ",
1307
+ ag.mainDirty.join(", ")
1308
+ ] }) : null,
1309
+ ag.files && ag.files.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-merge-files", children: ag.files.map((f) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-merge-file", children: [
1310
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-mono", children: f.path }),
1311
+ " ",
1312
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-diff-addn", children: [
1313
+ "+",
1314
+ f.adds
1315
+ ] }),
1316
+ " ",
1317
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-diff-deln", children: [
1318
+ "\u2212",
1319
+ f.dels
1320
+ ] })
1321
+ ] }, f.path)) }) : null,
1322
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DiffView, { patch: ag.patch }),
1323
+ ag.truncated ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "Diff truncated." }) : null
1324
+ ] }) : null })
1325
+ ] }) })
1326
+ }
1327
+ );
1328
+ }
852
1329
  function EmptyMain({ store }) {
853
1330
  const a = store.actions;
854
1331
  const workspaces = useNativeWorkspaces(store);
@@ -908,11 +1385,25 @@ function RoomsMain({ store }) {
908
1385
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-chat-header-title", children: room.name }),
909
1386
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-chat-header-sub", children: [
910
1387
  room.members.filter((m) => m.online !== false).length,
911
- " online \xB7 ",
1388
+ " online",
1389
+ room.local && room.local.agents.length ? ` \xB7 ${room.local.agents.length} local agent${room.local.agents.length === 1 ? "" : "s"}` : "",
1390
+ " \xB7 ",
912
1391
  room.public ? "public" : "private",
913
1392
  room.status !== "online" ? ` \xB7 room ${room.status}` : ""
914
1393
  ] })
915
1394
  ] }),
1395
+ (() => {
1396
+ const act = (room.runs || []).filter((r) => r.state === "running" || r.state === "queued" || r.state === "approval");
1397
+ if (!act.length) return null;
1398
+ const running = act.filter((r) => r.state === "running").length;
1399
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-stop-btn", "data-rm-stop": "", title: "Stop all local agent runs", onClick: () => store.actions.stop(room.roomId).then((r) => store.actions.toast(`Stopped ${r.stopped} run(s)`)).catch((e) => store.actions.toast(e.message)), children: [
1400
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StopIcon, {}),
1401
+ "Stop \xB7 ",
1402
+ running,
1403
+ " running",
1404
+ act.length > running ? `, ${act.length - running} queued` : ""
1405
+ ] });
1406
+ })(),
916
1407
  room.public ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-badge rm-badge-public", children: [
917
1408
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GlobeIcon, {}),
918
1409
  "Public"
@@ -920,10 +1411,10 @@ function RoomsMain({ store }) {
920
1411
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LockIcon, {}),
921
1412
  "Private"
922
1413
  ] }),
923
- room.transport === "lan" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-badge", children: "LAN/VPS" }) : null,
1414
+ room.transport === "local" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-badge rm-badge-local", children: "Local only" }) : room.transport === "lan" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-badge", children: "LAN/VPS" }) : null,
924
1415
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.StateDot, { state: room.status === "online" ? "done" : room.status === "connecting" ? "ongoing" : "error", size: 8 })
925
1416
  ] }),
926
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChatBody, { messages }),
1417
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChatBody, { messages, members: room.members }),
927
1418
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Composer, { room, store, draft, setDraft })
928
1419
  ] }),
929
1420
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -953,30 +1444,83 @@ function Field({ label, hint, children, htmlFor }) {
953
1444
  function CreateDialog({ store, d }) {
954
1445
  const s = useStore(store);
955
1446
  const a = store.actions;
956
- const [f, setF] = (0, import_react.useState)({ name: "", description: "", tags: "", public: false, mode: "lan", lanHostPort: "62.234.113.56:80" });
1447
+ const [tab, setTab] = (0, import_react.useState)("room");
1448
+ const [f, setF] = (0, import_react.useState)({
1449
+ name: "",
1450
+ description: "",
1451
+ tags: "",
1452
+ public: false,
1453
+ transport: "relay",
1454
+ // local | relay | lan | cloudflare
1455
+ lanHostPort: "62.234.113.56:80",
1456
+ turnLimit: 6,
1457
+ concurrency: 2,
1458
+ isolation: "auto",
1459
+ agents: []
1460
+ // draft agents: { key?, name, persona, systemPrompt, scope, moderator, reasoningEffort, approvalRequired, model }
1461
+ });
1462
+ const [draft, setDraft] = (0, import_react.useState)({ name: "", persona: "", systemPrompt: "", scope: "read", moderator: false, reasoningEffort: "medium", approvalRequired: false, model: "" });
1463
+ const [showCustom, setShowCustom] = (0, import_react.useState)(false);
957
1464
  const [err, setErr] = (0, import_react.useState)(null);
958
1465
  const [busy, setBusy] = (0, import_react.useState)(false);
959
1466
  const [lanPeers, setLanPeers] = (0, import_react.useState)([]);
1467
+ const [presets, setPresets] = (0, import_react.useState)([]);
960
1468
  const ok = f.name.trim() && !busy;
961
1469
  const [ws, setWs] = (0, import_react.useState)(d && d.workspace ? d.workspace : null);
1470
+ (0, import_react.useEffect)(() => {
1471
+ a.presets().then(setPresets).catch(() => {
1472
+ });
1473
+ }, []);
1474
+ const togglePreset = (p) => {
1475
+ const has = f.agents.some((x) => x.key === p.key);
1476
+ setF({
1477
+ ...f,
1478
+ agents: has ? f.agents.filter((x) => x.key !== p.key) : [...f.agents, { key: p.key, name: p.name, persona: p.persona, systemPrompt: p.systemPrompt, scope: p.scope, moderator: !!p.moderator, reasoningEffort: p.reasoningEffort || "medium", approvalRequired: false, model: "" }]
1479
+ });
1480
+ };
1481
+ const addCustomDraft = () => {
1482
+ if (!draft.name.trim()) {
1483
+ setErr("Custom agent needs a name");
1484
+ setTab("agents");
1485
+ return;
1486
+ }
1487
+ setF({
1488
+ ...f,
1489
+ agents: [...f.agents, { ...draft, name: draft.name.trim(), key: void 0 }]
1490
+ });
1491
+ setDraft({ name: "", persona: "", systemPrompt: "", scope: "read", moderator: false, reasoningEffort: "medium", approvalRequired: false, model: "" });
1492
+ setShowCustom(false);
1493
+ setErr(null);
1494
+ };
1495
+ const removeDraft = (i) => setF({ ...f, agents: f.agents.filter((_, j) => j !== i) });
962
1496
  const submit = async () => {
963
1497
  if (!ok) return;
964
1498
  if (!ws) {
965
1499
  setErr("Pick a workspace");
1500
+ setTab("room");
966
1501
  return;
967
1502
  }
968
1503
  setBusy(true);
969
1504
  setErr(null);
1505
+ const transport = f.transport;
1506
+ const localOnly = transport === "local";
1507
+ const mode = transport === "cloudflare" ? "relay" : "lan";
1508
+ const lanHostPort = transport === "relay" ? f.lanHostPort.trim() || "62.234.113.56:80" : transport === "lan" ? "" : void 0;
970
1509
  try {
971
1510
  await a.create({
972
1511
  name: f.name.trim(),
973
1512
  description: f.description.trim(),
974
1513
  tags: f.tags,
975
- public: f.public,
976
- mode: f.mode,
977
- lanHostPort: f.mode === "lan" ? f.lanHostPort.trim() : void 0,
1514
+ public: localOnly ? false : f.public,
1515
+ mode,
1516
+ lanHostPort,
978
1517
  workspacePath: ws.path,
979
- workspaceName: ws.title
1518
+ workspaceName: ws.title,
1519
+ localOnly,
1520
+ turnLimit: f.turnLimit,
1521
+ concurrency: f.concurrency,
1522
+ isolation: f.isolation,
1523
+ agents: f.agents.map((ag) => ag.key ? ag.key : { name: ag.name, persona: ag.persona, systemPrompt: ag.systemPrompt, scope: ag.scope, moderator: ag.moderator, reasoningEffort: ag.reasoningEffort, approvalRequired: ag.approvalRequired, model: ag.model || void 0 })
980
1524
  });
981
1525
  } catch (e) {
982
1526
  setErr(e.message);
@@ -991,6 +1535,11 @@ function CreateDialog({ store, d }) {
991
1535
  setErr(e.message);
992
1536
  }
993
1537
  };
1538
+ const tabs = [
1539
+ { id: "room", label: "Room" },
1540
+ { id: "agents", label: `Agents${f.agents.length ? ` (${f.agents.length})` : ""}` },
1541
+ { id: "limits", label: "Limits" }
1542
+ ];
994
1543
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
995
1544
  import_dsh_client_ui_primitives.Modal,
996
1545
  {
@@ -998,42 +1547,128 @@ function CreateDialog({ store, d }) {
998
1547
  onClose: a.closeDialog,
999
1548
  title: "Create room",
1000
1549
  closeLabel: "Close",
1001
- className: "rm-modal-wide",
1002
- description: ws ? `In workspace \u201C${ws.title}\u201D (${ws.path}). Agent cwd + MCP sandbox = this path. Default relay 62.234.113.56:80 \xB7 E2EE.` : "Pick a dsh workspace. Default relay 62.234.113.56:80 \xB7 E2EE.",
1550
+ className: "rm-modal-wide rm-modal-create rm-modal-scrollable",
1551
+ contentClassName: "rm-modal-scroll",
1552
+ description: ws ? `Workspace \u201C${ws.title}\u201D \xB7 default VPS relay 62.234.113.56:80 \xB7 E2EE. Every option below is editable.` : "Pick a dsh workspace. Every create option is on these tabs \u2014 nothing is hidden off-screen.",
1003
1553
  footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1004
1554
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Cancel" }),
1005
1555
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", disabled: !ok, onClick: submit, children: busy ? "Creating\u2026" : "Create" })
1006
1556
  ] }),
1007
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-form rm-form-wide", onKeyDown: (e) => e.key === "Enter" && e.target.tagName === "INPUT" && submit(), children: [
1008
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Workspace", htmlFor: "rm-create-ws", hint: "Native dsh workspace this room belongs to.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WorkspaceSelect, { store, id: "rm-create-ws", value: ws, onChange: setWs }) }),
1009
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Name", htmlFor: "rm-create-name", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-name-row", children: [
1010
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: f.name || "room", size: 36 }),
1011
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-name", autoFocus: true, placeholder: "e.g. release-crew", maxLength: 60, value: f.name, onChange: (e) => setF({ ...f, name: e.target.value }) })
1012
- ] }) }),
1013
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Transport", hint: "VPS directory works on LAN/VPS. Cloudflare is optional.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-mode-row", children: [
1014
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (f.mode === "lan" ? " is-on" : ""), onClick: () => setF({ ...f, mode: "lan", lanHostPort: f.lanHostPort || "62.234.113.56:80" }), children: "LAN / VPS" }),
1015
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (f.mode === "relay" ? " is-on" : ""), onClick: () => setF({ ...f, mode: "relay" }), children: "Cloudflare" })
1016
- ] }) }),
1017
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Description", htmlFor: "rm-create-desc", hint: "Shown to members and in public search.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-desc", placeholder: "e.g. Agents that review and ship releases", maxLength: 280, value: f.description, onChange: (e) => setF({ ...f, description: e.target.value }) }) }),
1018
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Tags", htmlFor: "rm-create-tags", hint: "Spaces or commas, up to 8.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-tags", placeholder: "e.g. review rust", value: f.tags, onChange: (e) => setF({ ...f, tags: e.target.value }) }) }),
1019
- f.mode === "lan" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Field, { label: "Relay host:port", htmlFor: "rm-lan-hp", hint: "Default 62.234.113.56:80. Blank = this machine + mDNS.", children: [
1020
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-lan-hp", placeholder: "62.234.113.56:80 or 100.x.x.x:7421", value: f.lanHostPort, onChange: (e) => setF({ ...f, lanHostPort: e.target.value }) }),
1021
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-lan-browse", children: [
1557
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-create", "data-rm-create": "", onKeyDown: (e) => e.key === "Enter" && e.target.tagName === "INPUT" && tab === "room" && submit(), children: [
1558
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-tabs", role: "tablist", "aria-label": "Create room sections", children: tabs.map((t) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", role: "tab", "aria-selected": tab === t.id, className: "rm-tab" + (tab === t.id ? " is-on" : ""), onClick: () => setTab(t.id), children: t.label }, t.id)) }),
1559
+ tab === "room" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-form rm-form-wide rm-create-pane", role: "tabpanel", children: [
1560
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Workspace", htmlFor: "rm-create-ws", hint: "Native dsh workspace this room belongs to.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WorkspaceSelect, { store, id: "rm-create-ws", value: ws, onChange: setWs }) }),
1561
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Name", htmlFor: "rm-create-name", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-name-row", children: [
1562
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: f.name || "room", size: 36 }),
1563
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-name", autoFocus: true, placeholder: "e.g. release-crew", maxLength: 60, value: f.name, onChange: (e) => setF({ ...f, name: e.target.value }) })
1564
+ ] }) }),
1565
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Transport", hint: "Local only = in-process (no network). Relay / LAN / Cloudflare for remote hosts.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-mode-row rm-mode-wrap", children: [
1566
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (f.transport === "local" ? " is-on" : ""), onClick: () => setF({ ...f, transport: "local", public: false }), children: "Local only" }),
1567
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (f.transport === "relay" ? " is-on" : ""), onClick: () => setF({ ...f, transport: "relay", lanHostPort: f.lanHostPort || "62.234.113.56:80" }), children: "Relay (VPS)" }),
1568
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (f.transport === "lan" ? " is-on" : ""), onClick: () => setF({ ...f, transport: "lan" }), children: "LAN (this machine)" }),
1569
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (f.transport === "cloudflare" ? " is-on" : ""), onClick: () => setF({ ...f, transport: "cloudflare" }), children: "Cloudflare" })
1570
+ ] }) }) }),
1571
+ f.transport === "relay" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Relay host:port", htmlFor: "rm-lan-hp", hint: "Default 62.234.113.56:80 (public directory).", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-lan-hp", placeholder: "62.234.113.56:80", value: f.lanHostPort, onChange: (e) => setF({ ...f, lanHostPort: e.target.value }) }) }) }) : null,
1572
+ f.transport === "lan" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "LAN relay", hint: "Blank host embeds a relay on this machine and advertises it over mDNS.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-lan-browse", children: [
1022
1573
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", onClick: browseLan, children: "Browse mDNS" }),
1023
- lanPeers.map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-lan-peer", onClick: () => setF({ ...f, lanHostPort: p.hostPort }), children: [
1574
+ lanPeers.map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-lan-peer", onClick: () => setF({ ...f, transport: "relay", lanHostPort: p.hostPort }), children: [
1024
1575
  p.hostPort,
1025
1576
  p.name ? ` \xB7 ${p.name}` : ""
1026
1577
  ] }, p.hostPort))
1578
+ ] }) }) }) : null,
1579
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Description", htmlFor: "rm-create-desc", hint: "Shown to members and in public search.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-desc", placeholder: "e.g. Agents that review and ship releases", maxLength: 280, value: f.description, onChange: (e) => setF({ ...f, description: e.target.value }) }) }),
1580
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Tags", htmlFor: "rm-create-tags", hint: "Spaces or commas, up to 8.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-tags", placeholder: "e.g. review rust", value: f.tags, onChange: (e) => setF({ ...f, tags: e.target.value }) }) }),
1581
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-span-2 rm-switch-row", children: [
1582
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1583
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "List in public directory" }),
1584
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: f.transport === "local" ? "Local-only rooms stay private (no network)." : f.public ? "Anyone can find and join \u2014 key is published with the listing." : "Private: invite only." })
1585
+ ] }),
1586
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: f.public && f.transport !== "local", onChange: (v) => setF({ ...f, public: v }), label: "List in public directory", disabled: f.transport === "local", title: f.transport === "local" ? "Local-only rooms stay private" : void 0 })
1027
1587
  ] })
1028
- ] }) }) : null,
1029
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-span-2 rm-switch-row", children: [
1030
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1031
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "List in public directory" }),
1032
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: f.public ? "Anyone can find and join \u2014 key is published with the listing." : "Private: invite only." })
1588
+ ] }) : null,
1589
+ tab === "agents" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-create-pane", role: "tabpanel", children: [
1590
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Presets" }),
1591
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "Shortcuts that seed a local agent. You can still add free-form agents below, and edit anyone after create." }),
1592
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-preset-row", children: (presets.length ? presets : [
1593
+ { key: "moderator", name: "Moderator", persona: "Breaks requests into tasks and summarizes", scope: "read", moderator: true, reasoningEffort: "medium", systemPrompt: "" },
1594
+ { key: "architect", name: "Architect", persona: "Designs the approach", scope: "read", reasoningEffort: "high", systemPrompt: "" },
1595
+ { key: "coder", name: "Coder", persona: "Implements in its own worktree", scope: "write", reasoningEffort: "medium", systemPrompt: "" },
1596
+ { key: "reviewer", name: "Reviewer", persona: "Reviews diffs", scope: "read", reasoningEffort: "high", systemPrompt: "" }
1597
+ ]).map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "rm-preset rm-preset-check" + (f.agents.some((x) => x.key === p.key) ? " is-on" : ""), children: [
1598
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", checked: f.agents.some((x) => x.key === p.key), onChange: () => togglePreset(p) }),
1599
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
1600
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: p.name }),
1601
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-preset-sub", children: p.persona })
1602
+ ] })
1603
+ ] }, p.key)) }),
1604
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-create-custom", children: [
1605
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-create-custom-head", children: [
1606
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1607
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Custom agents" }),
1608
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "Free-form name, persona, system prompt, model, scope, approval, moderator." })
1609
+ ] }),
1610
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {}), onClick: () => {
1611
+ setShowCustom(true);
1612
+ setErr(null);
1613
+ }, children: "Add custom" })
1614
+ ] }),
1615
+ f.agents.filter((x) => !x.key).length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "rm-draft-list", children: f.agents.map((ag, i) => ag.key ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: "rm-draft-item", children: [
1616
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: ag.name, size: 24 }),
1617
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-draft-main", children: [
1618
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: ag.name }),
1619
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
1620
+ ag.persona || "custom",
1621
+ " \xB7 ",
1622
+ ag.scope,
1623
+ ag.moderator ? " \xB7 moderator" : ""
1624
+ ] })
1625
+ ] }),
1626
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mini", "aria-label": `Remove ${ag.name}`, onClick: () => removeDraft(i), children: "\xD7" })
1627
+ ] }, i)) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "No custom agents yet \u2014 presets above, or Add custom." }),
1628
+ showCustom ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-draft-form rm-form rm-form-wide", children: [
1629
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Name", htmlFor: "rm-create-ag-name", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-ag-name", value: draft.name, maxLength: 40, onChange: (e) => setDraft({ ...draft, name: e.target.value }), placeholder: "e.g. Data wrangler" }) }),
1630
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Persona", htmlFor: "rm-create-ag-persona", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-ag-persona", value: draft.persona, maxLength: 200, onChange: (e) => setDraft({ ...draft, persona: e.target.value }), placeholder: "One-line role" }) }),
1631
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "System prompt", htmlFor: "rm-create-ag-sp", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("textarea", { id: "rm-create-ag-sp", className: "rm-input rm-textarea", rows: 3, value: draft.systemPrompt, maxLength: 4e3, onChange: (e) => setDraft({ ...draft, systemPrompt: e.target.value }) }) }) }),
1632
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Model", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { className: "rm-input", "aria-label": "Model", value: draft.model, onChange: (e) => setDraft({ ...draft, model: e.target.value }), children: MODEL_CHOICES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: m, children: m || "dsh default model" }, m)) }) }),
1633
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Reasoning effort", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { className: "rm-input", "aria-label": "Reasoning effort", value: draft.reasoningEffort, onChange: (e) => setDraft({ ...draft, reasoningEffort: e.target.value }), children: ["low", "medium", "high", "max"].map((x) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: x, children: x }, x)) }) }),
1634
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Workspace scope", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-mode-row", children: [
1635
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (draft.scope === "read" ? " is-on" : ""), onClick: () => setDraft({ ...draft, scope: "read" }), children: "Read-only" }),
1636
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (draft.scope === "write" ? " is-on" : ""), onClick: () => setDraft({ ...draft, scope: "write" }), children: "Read + write" })
1637
+ ] }) }),
1638
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-col", children: [
1639
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-row", children: [
1640
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Approval required" }) }),
1641
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: draft.approvalRequired, onChange: (v) => setDraft({ ...draft, approvalRequired: v }), label: "Approval required" })
1642
+ ] }),
1643
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-row", children: [
1644
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Moderator" }) }),
1645
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: draft.moderator, onChange: (v) => setDraft({ ...draft, moderator: v }), label: "Moderator" })
1646
+ ] })
1647
+ ] }),
1648
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-span-2 rm-actions", children: [
1649
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", onClick: () => setShowCustom(false), children: "Cancel" }),
1650
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "primary", onClick: addCustomDraft, children: "Add to room" })
1651
+ ] })
1652
+ ] }) : null
1033
1653
  ] }),
1034
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: f.public, onChange: (v) => setF({ ...f, public: v }), label: "List in public directory" })
1035
- ] }),
1036
- err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2 rm-error", children: err }) : null
1654
+ f.agents.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-note", style: { marginTop: 10 }, children: [
1655
+ "Will create: ",
1656
+ f.agents.map((x) => x.name).join(", "),
1657
+ ". Edit or add more after create from Local agents \u2192 Add local agent."
1658
+ ] }) : null
1659
+ ] }) : null,
1660
+ tab === "limits" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-form rm-form-wide rm-create-pane", role: "tabpanel", children: [
1661
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Parallel runs", htmlFor: "rm-create-conc", hint: "Max local agent runs at once (1\u201316). The rest wait in a visible queue.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-conc", type: "number", min: 1, max: 16, value: String(f.concurrency), onChange: (e) => setF({ ...f, concurrency: Math.min(16, Math.max(1, Number(e.target.value) || 1)) }) }) }),
1662
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Turn limit", htmlFor: "rm-create-turns", hint: "Max agent-to-agent hand-offs per task/thread (1\u2013100). A human message resets it.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-turns", type: "number", min: 1, max: 100, value: String(f.turnLimit), onChange: (e) => setF({ ...f, turnLimit: Math.min(100, Math.max(1, Number(e.target.value) || 6)) }) }) }),
1663
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Workspace isolation", hint: "Writing agents get their own git worktree by default when the workspace is a git repo.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { className: "rm-input", "aria-label": "Isolation mode", value: f.isolation, onChange: (e) => setF({ ...f, isolation: e.target.value }), children: [
1664
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "auto", children: "Auto \u2014 git worktree per writing agent, else file locks" }),
1665
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "worktree", children: "Git worktree per agent" }),
1666
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "locks", children: "File locks (shared workspace)" }),
1667
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "off", children: "Off (shared, no locks)" })
1668
+ ] }) }) }),
1669
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2 rm-note", children: "These apply as soon as the room has local agents. Change them later under Runs / Workspace isolation in the room panel." })
1670
+ ] }) : null,
1671
+ err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-error", style: { marginTop: 10 }, children: err }) : null
1037
1672
  ] })
1038
1673
  }
1039
1674
  );
@@ -1373,6 +2008,8 @@ function Overlay({ store }) {
1373
2008
  d?.kind === "config" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConfigDialog, { store, d }) : null,
1374
2009
  d?.kind === "workspace" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WorkspaceDialog, { store }) : null,
1375
2010
  d?.kind === "browse" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BrowseDialog, { store }) : null,
2011
+ d?.kind === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AgentDialog, { store, d }) : null,
2012
+ d?.kind === "merge" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MergeDialog, { store, d }) : null,
1376
2013
  s.toast ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Toast, { text: s.toast, onDone: store.actions.clearToast, holdMs: 2200 }) : null
1377
2014
  ] });
1378
2015
  }
@@ -1555,7 +2192,7 @@ var import_react = require("react");
1555
2192
  var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
1556
2193
 
1557
2194
  // src/client/rooms.css
1558
- var rooms_default = '/* Collab Rooms UI \u2014 original surface inside the dsh shell. Uses only dsh --dsw-* tokens.\n Independent Rooms surface. Prefix: rm- (rooms). */\n\n.rm-root {\n --rm-accent: var(--dsw-alias-state-business-primary);\n --rm-hover: var(--dsw-alias-interactive-bg-hover);\n --rm-border: var(--dsw-alias-border-l2);\n --rm-bg: var(--dsw-alias-bg-base);\n --rm-panel: var(--dsw-alias-bg-module-platform);\n --rm-label: var(--dsw-alias-label-primary);\n --rm-muted: var(--dsw-alias-label-secondary);\n --rm-quiet: var(--dsw-alias-label-tertiary);\n font: 13px/1.5 var(--dsw-font-family);\n color: var(--rm-label);\n box-sizing: border-box;\n}\n.rm-root *, .rm-root *::before, .rm-root *::after { box-sizing: border-box; }\n.rm-root button, .rm-root input, .rm-root textarea { font: inherit; }\n\n/* \u2500\u2500 sidebar (joined rooms + public search) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-side {\n display: flex; flex-direction: column; height: 100%; min-height: 0;\n padding: 0 8px 8px 0; gap: 8px;\n}\n.rm-side-toolbar {\n display: flex; align-items: center; gap: 6px; flex: none; padding: 4px 0 2px;\n}\n.rm-side-title {\n flex: 1; font-size: 12px; font-weight: 600; letter-spacing: .04em;\n text-transform: uppercase; color: var(--rm-quiet);\n}\n.rm-icon-btn {\n width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 8px; background: transparent; color: var(--rm-muted); cursor: pointer; padding: 0;\n}\n.rm-icon-btn:hover { background: var(--rm-hover); color: var(--rm-label); }\n.rm-search {\n display: flex; align-items: center; gap: 6px; height: 32px; padding: 0 10px;\n border: .5px solid var(--rm-border); border-radius: 8px; background: var(--rm-panel);\n}\n.rm-search input {\n flex: 1; min-width: 0; border: 0; background: transparent; outline: none; color: var(--rm-label); font-size: 13px;\n}\n.rm-search input::placeholder { color: var(--rm-quiet); }\n.rm-side-scroll { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 12px; }\n.rm-group-label { font-size: 11px; font-weight: 600; color: var(--rm-quiet); padding: 0 4px 4px; text-transform: uppercase; letter-spacing: .03em; }\n.rm-room-list { display: flex; flex-direction: column; gap: 2px; }\n.rm-room-item {\n display: flex; align-items: center; gap: 10px; width: 100%; padding: 8px 10px;\n border: 0; border-radius: 10px; background: transparent; color: inherit; cursor: pointer; text-align: left;\n}\n.rm-room-item:hover { background: var(--rm-hover); }\n.rm-room-item[aria-current="true"] { background: var(--rm-hover); }\n.rm-room-item-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }\n.rm-room-item-name { font-weight: 600; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-room-item-meta { font-size: 11.5px; color: var(--rm-quiet); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-room-item-trail { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 2px; }\n.rm-badge {\n display: inline-flex; align-items: center; gap: 3px; height: 18px; padding: 0 6px;\n border-radius: 999px; font-size: 10.5px; font-weight: 500; background: var(--rm-panel); color: var(--rm-muted);\n border: .5px solid var(--rm-border);\n}\n.rm-badge-public { color: var(--rm-accent); border-color: color-mix(in srgb, var(--rm-accent) 35%, transparent); background: color-mix(in srgb, var(--rm-accent) 8%, transparent); }\n.rm-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; background: var(--rm-quiet); }\n.rm-dot-online { background: var(--dsw-alias-state-success-primary, #2bb673); }\n.rm-dot-connecting { background: var(--dsw-alias-state-warn-primary, #e9a23b); }\n.rm-dot-offline { background: var(--dsw-alias-state-error-primary, #e5484d); }\n.rm-empty-hint { padding: 8px 10px; font-size: 12px; color: var(--rm-quiet); line-height: 1.45; }\n.rm-dir-item {\n display: flex; align-items: flex-start; gap: 10px; padding: 10px; border-radius: 10px;\n border: .5px solid var(--rm-border); background: var(--rm-bg); margin-bottom: 6px;\n}\n.rm-dir-item-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }\n.rm-dir-tags { display: flex; flex-wrap: wrap; gap: 4px; }\n.rm-tag { font-size: 11px; color: var(--rm-muted); background: var(--rm-hover); border-radius: 4px; padding: 0 5px; }\n.rm-menu {\n position: fixed; z-index: 1000; min-width: 180px; padding: 4px;\n border-radius: 10px; background: var(--dsw-alias-bg-layer-1, var(--rm-bg));\n border: .5px solid var(--rm-border);\n box-shadow: 0 8px 24px color-mix(in srgb, var(--rm-label) 12%, transparent);\n display: flex; flex-direction: column;\n}\n.rm-menu button {\n display: flex; align-items: center; gap: 8px; height: 32px; padding: 0 10px;\n border: 0; border-radius: 6px; background: transparent; color: var(--rm-label); cursor: pointer; text-align: left;\n}\n.rm-menu button:hover { background: var(--rm-hover); }\n\n/* \u2500\u2500 main: room workspace \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-main { display: flex; flex: 1; min-height: 0; height: 100%; width: 100%; background: var(--rm-bg); }\n.rm-workspace { display: flex; flex: 1; min-width: 0; min-height: 0; }\n.rm-chat { display: flex; flex-direction: column; flex: 1; min-width: 0; min-height: 0; }\n.rm-chat-header {\n flex: none; display: flex; align-items: center; gap: 10px; height: 48px;\n padding: 0 16px; border-bottom: .5px solid var(--rm-border);\n}\n.rm-chat-header-title { font-size: 14px; font-weight: 600; }\n.rm-chat-header-sub { font-size: 12px; color: var(--rm-quiet); }\n.rm-messages {\n flex: 1; min-height: 0; overflow: auto; padding: 16px 18px 8px;\n display: flex; flex-direction: column; gap: 2px;\n}\n.rm-day {\n align-self: center; margin: 10px 0 12px; padding: 2px 10px; border-radius: 999px;\n font-size: 11px; color: var(--rm-quiet); background: var(--dsw-alias-button-elevated-fill, var(--rm-panel));\n}\n.rm-msg {\n display: grid; grid-template-columns: 32px 1fr; gap: 0 10px; padding: 4px 6px; border-radius: 8px;\n max-width: 720px;\n}\n.rm-msg:hover { background: color-mix(in srgb, var(--rm-hover) 60%, transparent); }\n.rm-msg-self { margin-left: auto; grid-template-columns: 1fr; max-width: 640px; }\n.rm-msg-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 1px; }\n.rm-msg-author { font-size: 12px; font-weight: 600; color: var(--rm-muted); }\n.rm-msg-time { font-size: 11px; color: var(--rm-quiet); }\n.rm-msg-body { font-size: 13.5px; line-height: 1.55; white-space: pre-wrap; word-break: break-word; }\n.rm-msg-self .rm-msg-body {\n background: var(--dsw-alias-label-primary); color: var(--dsw-alias-label-primary-foreground);\n padding: 8px 12px; border-radius: 12px 12px 4px 12px; width: fit-content; margin-left: auto;\n}\n.rm-msg-event {\n grid-template-columns: 1fr; max-width: 560px; margin: 6px 0;\n padding: 8px 12px; border: .5px solid var(--rm-border); border-radius: 10px; background: var(--rm-panel);\n}\n.rm-mention { color: var(--rm-accent); font-weight: 500; }\n.rm-msg-self .rm-mention { color: inherit; text-decoration: underline; text-underline-offset: 2px; }\n.rm-task-card { display: flex; flex-direction: column; gap: 4px; }\n.rm-task-head { display: flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--rm-muted); }\n.rm-task-title { font-weight: 600; font-size: 13.5px; }\n.rm-composer {\n flex: none; display: flex; align-items: flex-end; gap: 8px; padding: 10px 16px 14px;\n border-top: .5px solid var(--rm-border);\n}\n.rm-composer-box {\n flex: 1; display: flex; align-items: flex-end; gap: 6px; min-height: 44px;\n padding: 8px 10px; border: .5px solid var(--rm-border); border-radius: 12px; background: var(--rm-panel);\n}\n.rm-composer-box:focus-within { border-color: color-mix(in srgb, var(--rm-accent) 50%, var(--rm-border)); }\n.rm-composer-box textarea {\n flex: 1; min-height: 28px; max-height: 120px; border: 0; background: transparent; outline: none;\n resize: none; color: var(--rm-label); line-height: 1.45;\n}\n.rm-composer-box textarea::placeholder { color: var(--rm-quiet); }\n.rm-composer-mode {\n width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 8px; background: transparent; color: var(--rm-muted); cursor: pointer; flex: none;\n}\n.rm-composer-mode:hover, .rm-composer-mode[aria-pressed="true"] {\n color: var(--rm-accent); background: color-mix(in srgb, var(--rm-accent) 12%, transparent);\n}\n.rm-chat-hint { margin: 48px auto; text-align: center; color: var(--rm-quiet); font-size: 13px; max-width: 360px; line-height: 1.5; }\n\n/* \u2500\u2500 detail column (members / invite / about) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-detail {\n flex: 0 0 280px; width: 280px; display: flex; flex-direction: column; min-height: 0;\n border-left: .5px solid var(--rm-border); background: var(--rm-bg); overflow: auto;\n}\n.rm-detail-section { padding: 14px 14px 8px; border-bottom: .5px solid var(--rm-border); }\n.rm-detail-section:last-child { border-bottom: 0; }\n.rm-detail-h {\n display: flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 600;\n text-transform: uppercase; letter-spacing: .04em; color: var(--rm-quiet); margin-bottom: 8px;\n}\n.rm-member {\n display: flex; align-items: center; gap: 8px; padding: 5px 4px; border-radius: 8px; min-height: 34px;\n}\n.rm-member:hover { background: var(--rm-hover); }\n.rm-member-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }\n.rm-member-actions { display: none; gap: 2px; }\n.rm-member:hover .rm-member-actions, .rm-member:focus-within .rm-member-actions { display: inline-flex; }\n.rm-mini {\n width: 24px; height: 24px; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 6px; background: transparent; color: var(--rm-muted); cursor: pointer; font-size: 12px;\n}\n.rm-mini:hover { background: var(--dsw-alias-interactive-bg-active); color: var(--rm-label); }\n.rm-task-row { display: flex; align-items: center; gap: 6px; padding: 4px; font-size: 12.5px; }\n.rm-task-row-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-about { display: grid; grid-template-columns: 88px 1fr; gap: 6px 8px; font-size: 12.5px; margin: 0; }\n.rm-about dt { color: var(--rm-quiet); margin: 0; }\n.rm-about dd { margin: 0; min-width: 0; overflow-wrap: anywhere; display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }\n.rm-mono { font-family: var(--dsw-font-markdown-code-block-font-family, ui-monospace, monospace); font-size: 11.5px; }\n.rm-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }\n.rm-danger { color: var(--dsw-alias-state-error-primary, #e5484d) !important; }\n.rm-note { font-size: 12px; color: var(--rm-quiet); line-height: 1.45; margin: 0 0 8px; }\n\n/* \u2500\u2500 empty / welcome \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-welcome-wrap {\n flex: 1; display: flex; align-items: center; justify-content: center; padding: 32px; background: var(--rm-bg);\n}\n.rm-welcome {\n width: min(420px, 100%); display: flex; flex-direction: column; gap: 10px;\n padding: 20px; border-radius: 14px; border: .5px solid var(--rm-border); background: var(--rm-panel);\n}\n.rm-welcome h1 { margin: 0; font-size: 16px; font-weight: 600; }\n.rm-welcome p { margin: 0; font-size: 13px; color: var(--rm-muted); line-height: 1.5; }\n.rm-welcome-actions { display: flex; flex-direction: column; gap: 6px; margin-top: 6px; }\n.rm-welcome-action {\n display: flex; align-items: flex-start; gap: 10px; width: 100%; padding: 10px 12px;\n border: .5px solid var(--rm-border); border-radius: 10px; background: var(--rm-bg);\n color: var(--rm-label); cursor: pointer; text-align: left;\n}\n.rm-welcome-action:hover { background: var(--rm-hover); }\n.rm-welcome-action b { display: block; font-size: 13px; }\n.rm-welcome-action span { display: block; font-size: 12px; color: var(--rm-quiet); margin-top: 2px; }\n\n/* \u2500\u2500 avatars \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-avatar {\n display: inline-flex; align-items: center; justify-content: center; flex: none;\n font-weight: 600; letter-spacing: .02em; user-select: none;\n}\n.rm-avatar-wrap { position: relative; display: inline-flex; flex: none; }\n\n/* \u2500\u2500 dialogs (inside dsh Modal) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-form { display: flex; flex-direction: column; gap: 14px; }\n.rm-field { display: flex; flex-direction: column; gap: 6px; }\n.rm-field-label { font-size: 13px; font-weight: 500; }\n.rm-field-hint { font-size: 12px; color: var(--rm-quiet); }\n.rm-name-row { display: flex; align-items: center; gap: 10px; }\n.rm-name-row > :last-child { flex: 1; }\n.rm-switch-row {\n display: flex; align-items: center; justify-content: space-between; gap: 14px;\n padding: 10px 12px; border-radius: 10px; background: var(--rm-panel);\n}\n.rm-error { color: var(--dsw-alias-state-error-primary, #e5484d); font-size: 12.5px; }\n.rm-invite {\n display: block; padding: 10px 12px; border-radius: 8px; background: var(--rm-panel);\n font-family: var(--dsw-font-markdown-code-block-font-family, ui-monospace, monospace);\n font-size: 12px; word-break: break-all; user-select: all;\n}\n\n/* Dual transport + approvals + agent config */\n.rm-mode-row { display: flex; gap: 8px; }\n.rm-mode-btn {\n flex: 1; padding: 10px 12px; border-radius: 10px; border: 1px solid var(--rm-border, #2a2f3a);\n background: transparent; color: inherit; cursor: pointer; font: inherit;\n}\n.rm-mode-btn.is-on { border-color: var(--rm-accent, #5b8cff); background: color-mix(in srgb, var(--rm-accent, #5b8cff) 16%, transparent); }\n.rm-lan-browse { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; align-items: center; }\n.rm-lan-peer {\n font-size: 12px; padding: 4px 8px; border-radius: 999px; border: 1px solid var(--rm-border, #2a2f3a);\n background: transparent; color: inherit; cursor: pointer;\n}\n.rm-approvals { display: flex; flex-direction: column; gap: 8px; padding: 10px 12px; border-bottom: 1px solid var(--rm-border, #2a2f3a); background: color-mix(in srgb, #f5a524 12%, transparent); }\n.rm-approval { display: flex; gap: 12px; align-items: center; justify-content: space-between; }\n.rm-approval-body { font-size: 13px; }\n.rm-approval-actions { display: flex; gap: 6px; flex-shrink: 0; }\n.rm-input { width: 100%; padding: 8px 10px; border-radius: 8px; border: 1px solid var(--rm-border, #2a2f3a); background: transparent; color: inherit; font: inherit; }\n\n.rm-ws-bar { display: flex; flex-direction: column; gap: 4px; padding: 8px 12px; border-bottom: 1px solid var(--rm-border, #2a2f3a); }\n.rm-ws-chip { display: inline-flex; align-items: center; gap: 6px; align-self: flex-start; padding: 4px 10px; border-radius: 999px; border: 1px solid var(--rm-border, #2a2f3a); background: transparent; color: inherit; cursor: pointer; font: inherit; font-size: 12.5px; }\n.rm-ws-path { font-size: 11px; color: var(--rm-quiet, #8b93a7); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-room-item.is-active { background: color-mix(in srgb, var(--rm-accent, #5b8cff) 12%, transparent); }\n\n/* Native-aligned room composer (mirrors dsh conversation control row) */\n.rm-composer-card {\n box-sizing: border-box;\n width: 100%;\n max-width: 920px;\n margin: 0 auto;\n border-radius: 16px;\n background: var(--rm-elev, var(--rm-panel, #12151c));\n border: 1px solid var(--rm-border, #2a2f3a);\n box-shadow: 0 1px 2px rgba(0,0,0,.06);\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 8px 0 6px;\n}\n.rm-composer-grow { position: relative; padding: 0 4px; }\n.rm-composer-grow textarea {\n box-sizing: border-box; width: 100%; min-height: 36px; max-height: 160px;\n resize: none; border: none; outline: none; background: transparent;\n color: inherit; font: inherit; font-size: 14px; line-height: 22px;\n padding: 4px 12px 0 14px;\n}\n.rm-composer-grow textarea:disabled { opacity: .55; cursor: not-allowed; }\n.rm-composer-row {\n display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;\n gap: 10px; min-width: 0; padding: 2px 8px 4px;\n}\n.rm-composer-tools, .rm-composer-modes, .rm-composer-trailing {\n display: flex; align-items: center; min-width: 0; gap: 8px;\n}\n.rm-composer-trailing { flex: none; margin-left: auto; }\n.rm-composer-add {\n width: 28px; height: 28px; border: none; border-radius: 999px;\n background: color-mix(in srgb, var(--rm-fg, #e8eaed) 8%, transparent);\n color: inherit; cursor: pointer; display: grid; place-items: center;\n}\n.rm-composer-add:hover, .rm-composer-add[aria-pressed="true"] {\n background: color-mix(in srgb, var(--rm-accent, #5b8cff) 22%, transparent);\n}\n.rm-composer-select {\n max-width: 220px; height: 28px; border: none; border-radius: 8px;\n background: transparent; color: var(--rm-quiet, #8b93a7);\n font: inherit; font-size: 13px; font-weight: 500; line-height: 20px;\n padding: 0 8px; cursor: pointer; white-space: nowrap;\n}\n.rm-composer-select:hover:not(:disabled) {\n background: color-mix(in srgb, var(--rm-fg, #e8eaed) 8%, transparent);\n}\n.rm-composer-select:disabled { opacity: .5; cursor: default; }\n.rm-composer-model-text { display: inline; }\n.rm-composer-model-icon { display: none; font-size: 10px; }\n.rm-composer-effort {\n margin-left: 6px; font-size: 11px; opacity: .75;\n text-transform: uppercase; letter-spacing: .02em;\n}\n.rm-composer-send {\n width: 34px; height: 34px; border: none; border-radius: 999px;\n background: var(--rm-accent, #5b8cff); color: #fff; cursor: pointer;\n display: grid; place-items: center; transform: translateY(-2px);\n}\n.rm-composer-send:hover:not(:disabled) { filter: brightness(1.08); }\n.rm-composer-send:disabled { opacity: .4; cursor: default; }\n.rm-composer-popwrap { position: relative; }\n.rm-composer-menu {\n position: absolute; bottom: calc(100% + 6px); left: 0; z-index: 40;\n min-width: 200px; max-height: 320px; overflow: auto;\n border: 1px solid var(--rm-border, #2a2f3a); border-radius: 12px;\n background: var(--rm-elev, #12151c); box-shadow: 0 8px 24px rgba(0,0,0,.28);\n padding: 6px; display: flex; flex-direction: column; gap: 2px;\n}\n.rm-composer-menu-wide { min-width: 280px; max-width: 340px; }\n.rm-composer-menu-label {\n font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em;\n color: var(--rm-quiet, #8b93a7); padding: 6px 8px 2px;\n}\n.rm-composer-menu button[role="menuitem"] {\n text-align: left; border: none; background: transparent; color: inherit;\n font: inherit; font-size: 13px; padding: 6px 8px; border-radius: 8px; cursor: pointer;\n}\n.rm-composer-menu button[role="menuitem"]:hover,\n.rm-composer-menu button[role="menuitem"].is-active {\n background: color-mix(in srgb, var(--rm-accent, #5b8cff) 16%, transparent);\n}\n.rm-composer-check {\n display: flex; align-items: center; gap: 8px; font-size: 13px; padding: 4px 8px; cursor: pointer;\n}\n.rm-grant-row {\n display: flex; align-items: center; justify-content: space-between; gap: 8px;\n padding: 2px 8px; font-size: 12.5px;\n}\n.rm-grant-row select {\n font: inherit; font-size: 12px; border-radius: 6px;\n border: 1px solid var(--rm-border, #2a2f3a); background: transparent; color: inherit; padding: 2px 6px;\n}\n.rm-peer-grant-block { border-top: 1px solid var(--rm-border, #2a2f3a); padding-top: 4px; margin-top: 4px; }\n.rm-peer-grant-name { font-size: 12px; font-weight: 600; padding: 4px 8px; overflow: hidden; text-overflow: ellipsis; }\n\n/* Hide legacy tight composer if still referenced */\n.rm-composer { display: none; }\n\n.rm-composer-grow { position: relative; }\n.rm-mention-menu {\n position: absolute; left: 8px; right: 8px; bottom: calc(100% + 6px); z-index: 50;\n max-height: 240px; overflow: auto;\n border: 1px solid var(--rm-border, #2a2f3a); border-radius: 12px;\n background: var(--rm-elev, #12151c); box-shadow: 0 10px 28px rgba(0,0,0,.32);\n padding: 4px; display: flex; flex-direction: column; gap: 2px;\n}\n.rm-mention-item {\n display: flex; align-items: center; gap: 8px; text-align: left;\n border: none; background: transparent; color: inherit; font: inherit;\n padding: 6px 8px; border-radius: 8px; cursor: pointer;\n}\n.rm-mention-item:hover, .rm-mention-item.is-active {\n background: color-mix(in srgb, var(--rm-accent, #5b8cff) 18%, transparent);\n}\n.rm-mention-item-body { display: flex; flex-direction: column; min-width: 0; flex: 1; }\n.rm-mention-item-name { font-size: 13px; font-weight: 600; }\n.rm-mention-item-meta { font-size: 11px; color: var(--rm-quiet, #8b93a7); }\n\n\n/* New Room shortcut under workspace (mirrors New Session affordance) */\n.rm-new-room {\n display: flex; align-items: center; justify-content: center; gap: 6px;\n width: 100%; height: 36px; margin-top: 8px;\n border: .5px solid var(--rm-border); border-radius: 10px;\n background: var(--dsw-alias-button-elevated-fill, var(--rm-panel));\n color: var(--rm-label); cursor: pointer; font-weight: 500; font-size: 13.5px;\n}\n.rm-new-room:hover { background: var(--dsw-alias-button-floating-hover, var(--rm-hover)); }\n.rm-new-room svg { flex: none; }\n\n/* Wide / landscape create + browse modals (overrides default 380px Modal card) */\n.rm-modal-wide {\n width: min(720px, calc(100vw - 48px)) !important;\n max-height: min(640px, calc(100vh - 48px));\n}\n.rm-form-wide {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px 18px;\n align-items: start;\n}\n.rm-form-wide .rm-span-2 { grid-column: 1 / -1; }\n.rm-browse { display: flex; flex-direction: column; gap: 12px; min-height: 280px; max-height: 420px; }\n.rm-browse-search { flex: none; }\n.rm-browse-status { font-size: 11.5px; color: var(--rm-quiet); white-space: nowrap; }\n.rm-browse-list { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 6px; }\n@media (max-width: 640px) {\n .rm-form-wide { grid-template-columns: 1fr; }\n .rm-modal-wide { width: min(420px, calc(100vw - 32px)) !important; }\n}\n\n\n/* \u2500\u2500 panel layout: rooms nav (grouped by native dsh workspace) + content \u2500\u2500 */\n.rm-main { flex-direction: row; }\n.rm-main-body { display: flex; flex-direction: column; flex: 1; min-width: 0; min-height: 0; }\n.rm-main-body > .rm-welcome-wrap { flex: 1; }\n.rm-main-body > .rm-workspace { flex: 1; }\n.rm-nav {\n flex: 0 0 248px; width: 248px; display: flex; flex-direction: column; gap: 8px; min-height: 0;\n padding: 10px 8px; border-right: .5px solid var(--rm-border); background: var(--rm-bg);\n}\n.rm-nav-head { display: flex; align-items: center; gap: 4px; padding: 0 4px; }\n.rm-nav-scroll { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 10px; }\n.rm-ws-group { display: flex; flex-direction: column; gap: 2px; }\n.rm-ws-head {\n display: flex; align-items: center; gap: 6px; height: 30px; padding: 0 2px 0 8px;\n font-size: 12.5px; font-weight: 600; color: var(--rm-muted);\n}\n.rm-ws-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-ws-add { width: 24px; height: 24px; }\n.rm-ws-empty { padding: 2px 10px 6px 30px; font-size: 11.5px; }\n.rm-nav .rm-room-item { padding: 6px 8px 6px 14px; }\n.rm-session-mark { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; color: var(--rm-accent, var(--dsw-alias-state-business-primary)); }\n';
2195
+ var rooms_default = '/* Collab Rooms UI \u2014 original surface inside the dsh shell. Uses only dsh --dsw-* tokens.\n Independent Rooms surface. Prefix: rm- (rooms). */\n\n.rm-root {\n --rm-accent: var(--dsw-alias-state-business-primary);\n --rm-hover: var(--dsw-alias-interactive-bg-hover);\n --rm-border: var(--dsw-alias-border-l2);\n --rm-bg: var(--dsw-alias-bg-base);\n --rm-panel: var(--dsw-alias-bg-module-platform);\n --rm-label: var(--dsw-alias-label-primary);\n --rm-muted: var(--dsw-alias-label-secondary);\n --rm-quiet: var(--dsw-alias-label-tertiary);\n font: 13px/1.5 var(--dsw-font-family);\n color: var(--rm-label);\n box-sizing: border-box;\n}\n.rm-root *, .rm-root *::before, .rm-root *::after { box-sizing: border-box; }\n.rm-root button, .rm-root input, .rm-root textarea { font: inherit; }\n\n/* \u2500\u2500 sidebar (joined rooms + public search) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-side {\n display: flex; flex-direction: column; height: 100%; min-height: 0;\n padding: 0 8px 8px 0; gap: 8px;\n}\n.rm-side-toolbar {\n display: flex; align-items: center; gap: 6px; flex: none; padding: 4px 0 2px;\n}\n.rm-side-title {\n flex: 1; font-size: 12px; font-weight: 600; letter-spacing: .04em;\n text-transform: uppercase; color: var(--rm-quiet);\n}\n.rm-icon-btn {\n width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 8px; background: transparent; color: var(--rm-muted); cursor: pointer; padding: 0;\n}\n.rm-icon-btn:hover { background: var(--rm-hover); color: var(--rm-label); }\n.rm-search {\n display: flex; align-items: center; gap: 6px; height: 32px; padding: 0 10px;\n border: .5px solid var(--rm-border); border-radius: 8px; background: var(--rm-panel);\n}\n.rm-search input {\n flex: 1; min-width: 0; border: 0; background: transparent; outline: none; color: var(--rm-label); font-size: 13px;\n}\n.rm-search input::placeholder { color: var(--rm-quiet); }\n.rm-side-scroll { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 12px; }\n.rm-group-label { font-size: 11px; font-weight: 600; color: var(--rm-quiet); padding: 0 4px 4px; text-transform: uppercase; letter-spacing: .03em; }\n.rm-room-list { display: flex; flex-direction: column; gap: 2px; }\n.rm-room-item {\n display: flex; align-items: center; gap: 10px; width: 100%; padding: 8px 10px;\n border: 0; border-radius: 10px; background: transparent; color: inherit; cursor: pointer; text-align: left;\n}\n.rm-room-item:hover { background: var(--rm-hover); }\n.rm-room-item[aria-current="true"] { background: var(--rm-hover); }\n.rm-room-item-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }\n.rm-room-item-name { font-weight: 600; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-room-item-meta { font-size: 11.5px; color: var(--rm-quiet); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-room-item-trail { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 2px; }\n.rm-badge {\n display: inline-flex; align-items: center; gap: 3px; height: 18px; padding: 0 6px;\n border-radius: 999px; font-size: 10.5px; font-weight: 500; background: var(--rm-panel); color: var(--rm-muted);\n border: .5px solid var(--rm-border);\n}\n.rm-badge-public { color: var(--rm-accent); border-color: color-mix(in srgb, var(--rm-accent) 35%, transparent); background: color-mix(in srgb, var(--rm-accent) 8%, transparent); }\n.rm-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; background: var(--rm-quiet); }\n.rm-dot-online { background: var(--dsw-alias-state-success-primary, #2bb673); }\n.rm-dot-connecting { background: var(--dsw-alias-state-warn-primary, #e9a23b); }\n.rm-dot-offline { background: var(--dsw-alias-state-error-primary, #e5484d); }\n.rm-empty-hint { padding: 8px 10px; font-size: 12px; color: var(--rm-quiet); line-height: 1.45; }\n.rm-dir-item {\n display: flex; align-items: flex-start; gap: 10px; padding: 10px; border-radius: 10px;\n border: .5px solid var(--rm-border); background: var(--rm-bg); margin-bottom: 6px;\n}\n.rm-dir-item-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }\n.rm-dir-tags { display: flex; flex-wrap: wrap; gap: 4px; }\n.rm-tag { font-size: 11px; color: var(--rm-muted); background: var(--rm-hover); border-radius: 4px; padding: 0 5px; }\n.rm-menu {\n position: fixed; z-index: 1000; min-width: 180px; padding: 4px;\n border-radius: 10px; background: var(--dsw-alias-bg-layer-1, var(--rm-bg));\n border: .5px solid var(--rm-border);\n box-shadow: 0 8px 24px color-mix(in srgb, var(--rm-label) 12%, transparent);\n display: flex; flex-direction: column;\n}\n.rm-menu button {\n display: flex; align-items: center; gap: 8px; height: 32px; padding: 0 10px;\n border: 0; border-radius: 6px; background: transparent; color: var(--rm-label); cursor: pointer; text-align: left;\n}\n.rm-menu button:hover { background: var(--rm-hover); }\n\n/* \u2500\u2500 main: room workspace \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-main { display: flex; flex: 1; min-height: 0; height: 100%; width: 100%; background: var(--rm-bg); }\n.rm-workspace { display: flex; flex: 1; min-width: 0; min-height: 0; }\n.rm-chat { display: flex; flex-direction: column; flex: 1; min-width: 0; min-height: 0; }\n.rm-chat-header {\n flex: none; display: flex; align-items: center; gap: 10px; height: 48px;\n padding: 0 16px; border-bottom: .5px solid var(--rm-border);\n}\n.rm-chat-header-title { font-size: 14px; font-weight: 600; }\n.rm-chat-header-sub { font-size: 12px; color: var(--rm-quiet); }\n.rm-messages {\n flex: 1; min-height: 0; overflow: auto; padding: 16px 18px 8px;\n display: flex; flex-direction: column; gap: 2px;\n}\n.rm-day {\n align-self: center; margin: 10px 0 12px; padding: 2px 10px; border-radius: 999px;\n font-size: 11px; color: var(--rm-quiet); background: var(--dsw-alias-button-elevated-fill, var(--rm-panel));\n}\n.rm-msg {\n display: grid; grid-template-columns: 32px 1fr; gap: 0 10px; padding: 4px 6px; border-radius: 8px;\n max-width: 720px;\n}\n.rm-msg:hover { background: color-mix(in srgb, var(--rm-hover) 60%, transparent); }\n.rm-msg-self { margin-left: auto; grid-template-columns: 1fr; max-width: 640px; }\n.rm-msg-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 1px; }\n.rm-msg-author { font-size: 12px; font-weight: 600; color: var(--rm-muted); }\n.rm-msg-time { font-size: 11px; color: var(--rm-quiet); }\n.rm-msg-body { font-size: 13.5px; line-height: 1.55; white-space: pre-wrap; word-break: break-word; }\n.rm-msg-self .rm-msg-body {\n background: var(--dsw-alias-label-primary); color: var(--dsw-alias-label-primary-foreground);\n padding: 8px 12px; border-radius: 12px 12px 4px 12px; width: fit-content; margin-left: auto;\n}\n.rm-msg-event {\n grid-template-columns: 1fr; max-width: 560px; margin: 6px 0;\n padding: 8px 12px; border: .5px solid var(--rm-border); border-radius: 10px; background: var(--rm-panel);\n}\n.rm-mention { color: var(--rm-accent); font-weight: 500; }\n.rm-msg-self .rm-mention { color: inherit; text-decoration: underline; text-underline-offset: 2px; }\n.rm-task-card { display: flex; flex-direction: column; gap: 4px; }\n.rm-task-head { display: flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--rm-muted); }\n.rm-task-title { font-weight: 600; font-size: 13.5px; }\n.rm-composer {\n flex: none; display: flex; align-items: flex-end; gap: 8px; padding: 10px 16px 14px;\n border-top: .5px solid var(--rm-border);\n}\n.rm-composer-box {\n flex: 1; display: flex; align-items: flex-end; gap: 6px; min-height: 44px;\n padding: 8px 10px; border: .5px solid var(--rm-border); border-radius: 12px; background: var(--rm-panel);\n}\n.rm-composer-box:focus-within { border-color: color-mix(in srgb, var(--rm-accent) 50%, var(--rm-border)); }\n.rm-composer-box textarea {\n flex: 1; min-height: 28px; max-height: 120px; border: 0; background: transparent; outline: none;\n resize: none; color: var(--rm-label); line-height: 1.45;\n}\n.rm-composer-box textarea::placeholder { color: var(--rm-quiet); }\n.rm-composer-mode {\n width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 8px; background: transparent; color: var(--rm-muted); cursor: pointer; flex: none;\n}\n.rm-composer-mode:hover, .rm-composer-mode[aria-pressed="true"] {\n color: var(--rm-accent); background: color-mix(in srgb, var(--rm-accent) 12%, transparent);\n}\n.rm-chat-hint { margin: 48px auto; text-align: center; color: var(--rm-quiet); font-size: 13px; max-width: 360px; line-height: 1.5; }\n\n/* \u2500\u2500 detail column (members / invite / about) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-detail {\n flex: 0 0 280px; width: 280px; display: flex; flex-direction: column; min-height: 0;\n border-left: .5px solid var(--rm-border); background: var(--rm-bg); overflow: auto;\n}\n.rm-detail-section { padding: 14px 14px 8px; border-bottom: .5px solid var(--rm-border); }\n.rm-detail-section:last-child { border-bottom: 0; }\n.rm-detail-h {\n display: flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 600;\n text-transform: uppercase; letter-spacing: .04em; color: var(--rm-quiet); margin-bottom: 8px;\n}\n.rm-member {\n display: flex; align-items: center; gap: 8px; padding: 5px 4px; border-radius: 8px; min-height: 34px;\n}\n.rm-member:hover { background: var(--rm-hover); }\n.rm-member-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }\n.rm-member-actions { display: none; gap: 2px; }\n.rm-member:hover .rm-member-actions, .rm-member:focus-within .rm-member-actions { display: inline-flex; }\n.rm-mini {\n width: 24px; height: 24px; display: inline-flex; align-items: center; justify-content: center;\n border: 0; border-radius: 6px; background: transparent; color: var(--rm-muted); cursor: pointer; font-size: 12px;\n}\n.rm-mini:hover { background: var(--dsw-alias-interactive-bg-active); color: var(--rm-label); }\n.rm-task-row { display: flex; align-items: center; gap: 6px; padding: 4px; font-size: 12.5px; }\n.rm-task-row-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-about { display: grid; grid-template-columns: 88px 1fr; gap: 6px 8px; font-size: 12.5px; margin: 0; }\n.rm-about dt { color: var(--rm-quiet); margin: 0; }\n.rm-about dd { margin: 0; min-width: 0; overflow-wrap: anywhere; display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }\n.rm-mono { font-family: var(--dsw-font-markdown-code-block-font-family, ui-monospace, monospace); font-size: 11.5px; }\n.rm-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }\n.rm-danger { color: var(--dsw-alias-state-error-primary, #e5484d) !important; }\n.rm-note { font-size: 12px; color: var(--rm-quiet); line-height: 1.45; margin: 0 0 8px; }\n\n/* \u2500\u2500 empty / welcome \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-welcome-wrap {\n flex: 1; display: flex; align-items: center; justify-content: center; padding: 32px; background: var(--rm-bg);\n}\n.rm-welcome {\n width: min(420px, 100%); display: flex; flex-direction: column; gap: 10px;\n padding: 20px; border-radius: 14px; border: .5px solid var(--rm-border); background: var(--rm-panel);\n}\n.rm-welcome h1 { margin: 0; font-size: 16px; font-weight: 600; }\n.rm-welcome p { margin: 0; font-size: 13px; color: var(--rm-muted); line-height: 1.5; }\n.rm-welcome-actions { display: flex; flex-direction: column; gap: 6px; margin-top: 6px; }\n.rm-welcome-action {\n display: flex; align-items: flex-start; gap: 10px; width: 100%; padding: 10px 12px;\n border: .5px solid var(--rm-border); border-radius: 10px; background: var(--rm-bg);\n color: var(--rm-label); cursor: pointer; text-align: left;\n}\n.rm-welcome-action:hover { background: var(--rm-hover); }\n.rm-welcome-action b { display: block; font-size: 13px; }\n.rm-welcome-action span { display: block; font-size: 12px; color: var(--rm-quiet); margin-top: 2px; }\n\n/* \u2500\u2500 avatars \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-avatar {\n display: inline-flex; align-items: center; justify-content: center; flex: none;\n font-weight: 600; letter-spacing: .02em; user-select: none;\n}\n.rm-avatar-wrap { position: relative; display: inline-flex; flex: none; }\n\n/* \u2500\u2500 dialogs (inside dsh Modal) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-form { display: flex; flex-direction: column; gap: 14px; }\n.rm-field { display: flex; flex-direction: column; gap: 6px; }\n.rm-field-label { font-size: 13px; font-weight: 500; }\n.rm-field-hint { font-size: 12px; color: var(--rm-quiet); }\n.rm-name-row { display: flex; align-items: center; gap: 10px; }\n.rm-name-row > :last-child { flex: 1; }\n.rm-switch-row {\n display: flex; align-items: center; justify-content: space-between; gap: 14px;\n padding: 10px 12px; border-radius: 10px; background: var(--rm-panel);\n}\n.rm-error { color: var(--dsw-alias-state-error-primary, #e5484d); font-size: 12.5px; }\n.rm-invite {\n display: block; padding: 10px 12px; border-radius: 8px; background: var(--rm-panel);\n font-family: var(--dsw-font-markdown-code-block-font-family, ui-monospace, monospace);\n font-size: 12px; word-break: break-all; user-select: all;\n}\n\n/* Dual transport + approvals + agent config */\n.rm-mode-row { display: flex; gap: 8px; }\n.rm-mode-btn {\n flex: 1; padding: 10px 12px; border-radius: 10px; border: 1px solid var(--rm-border, #2a2f3a);\n background: transparent; color: inherit; cursor: pointer; font: inherit;\n}\n.rm-mode-btn.is-on { border-color: var(--rm-accent, #5b8cff); background: color-mix(in srgb, var(--rm-accent, #5b8cff) 16%, transparent); }\n.rm-lan-browse { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; align-items: center; }\n.rm-lan-peer {\n font-size: 12px; padding: 4px 8px; border-radius: 999px; border: 1px solid var(--rm-border, #2a2f3a);\n background: transparent; color: inherit; cursor: pointer;\n}\n.rm-approvals { display: flex; flex-direction: column; gap: 8px; padding: 10px 12px; border-bottom: 1px solid var(--rm-border, #2a2f3a); background: color-mix(in srgb, #f5a524 12%, transparent); }\n.rm-approval { display: flex; gap: 12px; align-items: center; justify-content: space-between; }\n.rm-approval-body { font-size: 13px; }\n.rm-approval-actions { display: flex; gap: 6px; flex-shrink: 0; }\n.rm-input { width: 100%; padding: 8px 10px; border-radius: 8px; border: 1px solid var(--rm-border, #2a2f3a); background: transparent; color: inherit; font: inherit; }\n\n.rm-ws-bar { display: flex; flex-direction: column; gap: 4px; padding: 8px 12px; border-bottom: 1px solid var(--rm-border, #2a2f3a); }\n.rm-ws-chip { display: inline-flex; align-items: center; gap: 6px; align-self: flex-start; padding: 4px 10px; border-radius: 999px; border: 1px solid var(--rm-border, #2a2f3a); background: transparent; color: inherit; cursor: pointer; font: inherit; font-size: 12.5px; }\n.rm-ws-path { font-size: 11px; color: var(--rm-quiet, #8b93a7); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-room-item.is-active { background: color-mix(in srgb, var(--rm-accent, #5b8cff) 12%, transparent); }\n\n/* Native-aligned room composer (mirrors dsh conversation control row) */\n.rm-composer-card {\n box-sizing: border-box;\n width: 100%;\n max-width: 920px;\n margin: 0 auto;\n border-radius: 16px;\n background: var(--rm-elev, var(--rm-panel, #12151c));\n border: 1px solid var(--rm-border, #2a2f3a);\n box-shadow: 0 1px 2px rgba(0,0,0,.06);\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 8px 0 6px;\n}\n.rm-composer-grow { position: relative; padding: 0 4px; }\n.rm-composer-grow textarea {\n box-sizing: border-box; width: 100%; min-height: 36px; max-height: 160px;\n resize: none; border: none; outline: none; background: transparent;\n color: inherit; font: inherit; font-size: 14px; line-height: 22px;\n padding: 4px 12px 0 14px;\n}\n.rm-composer-grow textarea:disabled { opacity: .55; cursor: not-allowed; }\n.rm-composer-row {\n display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;\n gap: 10px; min-width: 0; padding: 2px 8px 4px;\n}\n.rm-composer-tools, .rm-composer-modes, .rm-composer-trailing {\n display: flex; align-items: center; min-width: 0; gap: 8px;\n}\n.rm-composer-trailing { flex: none; margin-left: auto; }\n.rm-composer-add {\n width: 28px; height: 28px; border: none; border-radius: 999px;\n background: color-mix(in srgb, var(--rm-fg, #e8eaed) 8%, transparent);\n color: inherit; cursor: pointer; display: grid; place-items: center;\n}\n.rm-composer-add:hover, .rm-composer-add[aria-pressed="true"] {\n background: color-mix(in srgb, var(--rm-accent, #5b8cff) 22%, transparent);\n}\n.rm-composer-select {\n max-width: 220px; height: 28px; border: none; border-radius: 8px;\n background: transparent; color: var(--rm-quiet, #8b93a7);\n font: inherit; font-size: 13px; font-weight: 500; line-height: 20px;\n padding: 0 8px; cursor: pointer; white-space: nowrap;\n}\n.rm-composer-select:hover:not(:disabled) {\n background: color-mix(in srgb, var(--rm-fg, #e8eaed) 8%, transparent);\n}\n.rm-composer-select:disabled { opacity: .5; cursor: default; }\n.rm-composer-model-text { display: inline; }\n.rm-composer-model-icon { display: none; font-size: 10px; }\n.rm-composer-effort {\n margin-left: 6px; font-size: 11px; opacity: .75;\n text-transform: uppercase; letter-spacing: .02em;\n}\n.rm-composer-send {\n width: 34px; height: 34px; border: none; border-radius: 999px;\n background: var(--rm-accent, #5b8cff); color: #fff; cursor: pointer;\n display: grid; place-items: center; transform: translateY(-2px);\n}\n.rm-composer-send:hover:not(:disabled) { filter: brightness(1.08); }\n.rm-composer-send:disabled { opacity: .4; cursor: default; }\n.rm-composer-popwrap { position: relative; }\n.rm-composer-menu {\n position: absolute; bottom: calc(100% + 6px); left: 0; z-index: 40;\n min-width: 200px; max-height: 320px; overflow: auto;\n border: 1px solid var(--rm-border, #2a2f3a); border-radius: 12px;\n background: var(--rm-elev, #12151c); box-shadow: 0 8px 24px rgba(0,0,0,.28);\n padding: 6px; display: flex; flex-direction: column; gap: 2px;\n}\n.rm-composer-menu-wide { min-width: 280px; max-width: 340px; }\n.rm-composer-menu-label {\n font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em;\n color: var(--rm-quiet, #8b93a7); padding: 6px 8px 2px;\n}\n.rm-composer-menu button[role="menuitem"] {\n text-align: left; border: none; background: transparent; color: inherit;\n font: inherit; font-size: 13px; padding: 6px 8px; border-radius: 8px; cursor: pointer;\n}\n.rm-composer-menu button[role="menuitem"]:hover,\n.rm-composer-menu button[role="menuitem"].is-active {\n background: color-mix(in srgb, var(--rm-accent, #5b8cff) 16%, transparent);\n}\n.rm-composer-check {\n display: flex; align-items: center; gap: 8px; font-size: 13px; padding: 4px 8px; cursor: pointer;\n}\n.rm-grant-row {\n display: flex; align-items: center; justify-content: space-between; gap: 8px;\n padding: 2px 8px; font-size: 12.5px;\n}\n.rm-grant-row select {\n font: inherit; font-size: 12px; border-radius: 6px;\n border: 1px solid var(--rm-border, #2a2f3a); background: transparent; color: inherit; padding: 2px 6px;\n}\n.rm-peer-grant-block { border-top: 1px solid var(--rm-border, #2a2f3a); padding-top: 4px; margin-top: 4px; }\n.rm-peer-grant-name { font-size: 12px; font-weight: 600; padding: 4px 8px; overflow: hidden; text-overflow: ellipsis; }\n\n/* Hide legacy tight composer if still referenced */\n.rm-composer { display: none; }\n\n.rm-composer-grow { position: relative; }\n.rm-mention-menu {\n position: absolute; left: 8px; right: 8px; bottom: calc(100% + 6px); z-index: 50;\n max-height: 240px; overflow: auto;\n border: 1px solid var(--rm-border, #2a2f3a); border-radius: 12px;\n background: var(--rm-elev, #12151c); box-shadow: 0 10px 28px rgba(0,0,0,.32);\n padding: 4px; display: flex; flex-direction: column; gap: 2px;\n}\n.rm-mention-item {\n display: flex; align-items: center; gap: 8px; text-align: left;\n border: none; background: transparent; color: inherit; font: inherit;\n padding: 6px 8px; border-radius: 8px; cursor: pointer;\n}\n.rm-mention-item:hover, .rm-mention-item.is-active {\n background: color-mix(in srgb, var(--rm-accent, #5b8cff) 18%, transparent);\n}\n.rm-mention-item-body { display: flex; flex-direction: column; min-width: 0; flex: 1; }\n.rm-mention-item-name { font-size: 13px; font-weight: 600; }\n.rm-mention-item-meta { font-size: 11px; color: var(--rm-quiet, #8b93a7); }\n\n\n/* New Room shortcut under workspace (mirrors New Session affordance) */\n.rm-new-room {\n display: flex; align-items: center; justify-content: center; gap: 6px;\n width: 100%; height: 36px; margin-top: 8px;\n border: .5px solid var(--rm-border); border-radius: 10px;\n background: var(--dsw-alias-button-elevated-fill, var(--rm-panel));\n color: var(--rm-label); cursor: pointer; font-weight: 500; font-size: 13.5px;\n}\n.rm-new-room:hover { background: var(--dsw-alias-button-floating-hover, var(--rm-hover)); }\n.rm-new-room svg { flex: none; }\n\n/* Wide / landscape create + browse modals (overrides default 380px Modal card) */\n.rm-modal-wide {\n width: min(720px, calc(100vw - 48px)) !important;\n max-height: min(640px, calc(100vh - 48px));\n}\n.rm-form-wide {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px 18px;\n align-items: start;\n}\n.rm-form-wide .rm-span-2 { grid-column: 1 / -1; }\n.rm-browse { display: flex; flex-direction: column; gap: 12px; min-height: 280px; max-height: 420px; }\n.rm-browse-search { flex: none; }\n.rm-browse-status { font-size: 11.5px; color: var(--rm-quiet); white-space: nowrap; }\n.rm-browse-list { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 6px; }\n@media (max-width: 640px) {\n .rm-form-wide { grid-template-columns: 1fr; }\n .rm-modal-wide { width: min(420px, calc(100vw - 32px)) !important; }\n}\n\n\n/* \u2500\u2500 panel layout: rooms nav (grouped by native dsh workspace) + content \u2500\u2500 */\n.rm-main { flex-direction: row; }\n.rm-main-body { display: flex; flex-direction: column; flex: 1; min-width: 0; min-height: 0; }\n.rm-main-body > .rm-welcome-wrap { flex: 1; }\n.rm-main-body > .rm-workspace { flex: 1; }\n.rm-nav {\n flex: 0 0 248px; width: 248px; display: flex; flex-direction: column; gap: 8px; min-height: 0;\n padding: 10px 8px; border-right: .5px solid var(--rm-border); background: var(--rm-bg);\n}\n.rm-nav-head { display: flex; align-items: center; gap: 4px; padding: 0 4px; }\n.rm-nav-scroll { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 10px; }\n.rm-ws-group { display: flex; flex-direction: column; gap: 2px; }\n.rm-ws-head {\n display: flex; align-items: center; gap: 6px; height: 30px; padding: 0 2px 0 8px;\n font-size: 12.5px; font-weight: 600; color: var(--rm-muted);\n}\n.rm-ws-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.rm-ws-add { width: 24px; height: 24px; }\n.rm-ws-empty { padding: 2px 10px 6px 30px; font-size: 11.5px; }\n.rm-nav .rm-room-item { padding: 6px 8px 6px 14px; }\n.rm-session-mark { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; color: var(--rm-accent, var(--dsw-alias-state-business-primary)); }\n\n/* \u2500\u2500 local multi-agent (0.3) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-dot-busy { background: var(--dsw-alias-state-warning-primary, #f0a020); }\n.rm-agent-tag { display: inline-flex; align-items: center; gap: 3px; margin-left: 6px; padding: 0 6px; border-radius: 999px; font-size: 10.5px; line-height: 17px; color: var(--rm-accent, #5b8cff); background: color-mix(in srgb, var(--rm-accent, #5b8cff) 12%, transparent); white-space: nowrap; }\n.rm-msg-id { margin-left: 6px; font-size: 11px; color: var(--rm-quiet); font-family: var(--dsw-font-mono, ui-monospace, monospace); }\n.rm-agent-row { display: flex; align-items: center; gap: 8px; padding: 6px 4px; border-radius: 8px; }\n.rm-agent-row:hover { background: var(--rm-hover); }\n.rm-agent-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }\n.rm-agent-name { font-size: 13px; font-weight: 600; color: var(--rm-label); display: flex; align-items: center; }\n.rm-agent-sub { font-size: 11px; color: var(--rm-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.rm-agent-row .rm-member-actions { display: inline-flex !important; opacity: 0.65; }\n.rm-agent-row:hover .rm-member-actions { opacity: 1; }\n.rm-num-row { display: flex; gap: 8px; margin: 4px 0 8px; }\n.rm-num-setting { flex: 1; display: flex; flex-direction: column; gap: 3px; font-size: 11px; color: var(--rm-muted); }\n.rm-num-setting input { width: 100%; box-sizing: border-box; padding: 5px 8px; border: 1px solid var(--rm-border); border-radius: 7px; background: var(--rm-bg); color: var(--rm-label); font: inherit; font-size: 13px; }\n.rm-runs { display: flex; flex-direction: column; gap: 4px; }\n.rm-run { display: grid; grid-template-columns: 22px auto 1fr auto; align-items: center; gap: 6px; padding: 5px 6px; border-radius: 7px; font-size: 12px; border: 1px solid var(--rm-border); }\n.rm-run-running { border-color: color-mix(in srgb, var(--dsw-alias-state-warning-primary, #f0a020) 55%, transparent); background: color-mix(in srgb, var(--dsw-alias-state-warning-primary, #f0a020) 8%, transparent); }\n.rm-run-queued, .rm-run-approval { border-style: dashed; }\n.rm-run-done, .rm-run-stopped, .rm-run-failed { opacity: 0.7; }\n.rm-run-pos { font-size: 11px; color: var(--rm-quiet); text-align: center; font-variant-numeric: tabular-nums; }\n.rm-run-who { font-weight: 600; color: var(--rm-label); white-space: nowrap; }\n.rm-run-what { color: var(--rm-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }\n.rm-stop-btn { display: inline-flex; align-items: center; gap: 5px; height: 26px; padding: 0 10px; border-radius: 999px; border: 1px solid color-mix(in srgb, var(--dsw-alias-state-danger-primary, #e5484d) 60%, transparent); background: color-mix(in srgb, var(--dsw-alias-state-danger-primary, #e5484d) 10%, transparent); color: var(--dsw-alias-state-danger-primary, #e5484d); font-size: 12px; cursor: pointer; white-space: nowrap; }\n.rm-stop-btn:hover { background: color-mix(in srgb, var(--dsw-alias-state-danger-primary, #e5484d) 18%, transparent); }\n.rm-badge-local { color: var(--dsw-alias-state-success-primary, #2bb673); }\n.rm-session-agent { color: var(--rm-accent, #5b8cff); }\n.rm-preset-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px; }\n.rm-preset { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; min-width: 150px; flex: 1; padding: 8px 10px; border: 1px solid var(--rm-border); border-radius: 10px; background: transparent; color: var(--rm-label); text-align: left; cursor: pointer; font: inherit; font-size: 12px; }\n.rm-preset span { color: var(--rm-muted); font-size: 11px; line-height: 1.35; }\n.rm-preset.is-on { border-color: var(--rm-accent, #5b8cff); background: color-mix(in srgb, var(--rm-accent, #5b8cff) 12%, transparent); }\n.rm-preset-check { flex-direction: row; align-items: center; gap: 8px; min-width: 120px; flex: 0 1 auto; }\n.rm-local-box { display: flex; flex-direction: column; gap: 8px; padding: 10px 12px; border: 1px dashed var(--rm-border); border-radius: 12px; }\n.rm-textarea { width: 100%; box-sizing: border-box; min-height: 84px; resize: vertical; font: inherit; font-size: 13px; padding: 8px 10px; }\n.rm-switch-col { display: flex; flex-direction: column; gap: 6px; }\n.rm-modal-xl { width: min(1180px, 96vw) !important; max-width: 96vw !important; }\n.rm-merge { display: grid; grid-template-columns: 260px 1fr; gap: 14px; min-height: 420px; }\n.rm-merge-list { display: flex; flex-direction: column; gap: 6px; border-right: 1px solid var(--rm-border); padding-right: 12px; }\n.rm-merge-agent { display: flex; align-items: center; gap: 8px; padding: 8px; border: 1px solid transparent; border-radius: 10px; background: transparent; color: var(--rm-label); cursor: pointer; text-align: left; font: inherit; }\n.rm-merge-agent:hover { background: var(--rm-hover); }\n.rm-merge-agent.is-on { border-color: var(--rm-accent, #5b8cff); background: color-mix(in srgb, var(--rm-accent, #5b8cff) 10%, transparent); }\n.rm-merge-agent-body { flex: 1; min-width: 0; display: flex; flex-direction: column; font-size: 12px; }\n.rm-merge-agent-body .rm-mono { font-size: 10.5px; color: var(--rm-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.rm-merge-main { min-width: 0; display: flex; flex-direction: column; gap: 8px; }\n.rm-merge-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; font-size: 13px; }\n.rm-merge-files { display: flex; flex-wrap: wrap; gap: 6px 14px; font-size: 12px; }\n.rm-confirm-text { font-size: 12px; color: var(--rm-label); margin-right: 4px; }\n.rm-diff { margin: 0; max-height: 52vh; overflow: auto; padding: 8px 0; border: 1px solid var(--rm-border); border-radius: 10px; background: var(--rm-panel); font-family: var(--dsw-font-mono, ui-monospace, monospace); font-size: 12px; line-height: 1.5; }\n.rm-diff > div { padding: 0 12px; white-space: pre; }\n.rm-diff-add { background: color-mix(in srgb, #2bb673 16%, transparent); }\n.rm-diff-del { background: color-mix(in srgb, #e5484d 16%, transparent); }\n.rm-diff-hunk { color: var(--rm-accent, #5b8cff); }\n.rm-diff-head, .rm-diff-file { color: var(--rm-muted); font-weight: 600; }\n.rm-diff-addn { color: #2bb673; }\n.rm-diff-deln { color: #e5484d; }\n.rm-ok { padding: 8px 10px; border-radius: 8px; font-size: 12.5px; color: var(--dsw-alias-state-success-primary, #2bb673); background: color-mix(in srgb, #2bb673 10%, transparent); }\n\n/* \u2500\u2500 create-room dialog (0.3.1): wide, scrollable, tabbed \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.rm-modal-scrollable {\n width: min(920px, calc(100vw - 48px)) !important;\n max-width: 96vw !important;\n /* Bound height so the flex child (.rm-modal-scroll) can scroll instead of clipping. */\n height: min(720px, 100%) !important;\n max-height: 100% !important;\n}\n.rm-modal-create { width: min(960px, calc(100vw - 48px)) !important; }\n.rm-modal-scroll {\n flex: 1 1 auto;\n min-height: 0;\n overflow-x: hidden;\n overflow-y: auto;\n overscroll-behavior: contain;\n}\n.rm-create { display: flex; flex-direction: column; gap: 12px; }\n.rm-tabs {\n display: flex; gap: 4px; padding: 2px; border-radius: 10px;\n background: color-mix(in srgb, var(--rm-border) 55%, transparent);\n flex-wrap: wrap;\n}\n.rm-tab {\n flex: 1 1 auto; min-width: 88px; height: 32px; padding: 0 12px; border: 0; border-radius: 8px;\n background: transparent; color: var(--rm-muted); font: inherit; font-size: 13px; font-weight: 560;\n cursor: pointer;\n}\n.rm-tab.is-on { background: var(--rm-panel, var(--dsw-alias-bg-layer-2)); color: var(--rm-label); box-shadow: 0 0 0 1px var(--rm-border); }\n.rm-create-pane { min-height: 280px; }\n.rm-mode-wrap { flex-wrap: wrap; }\n.rm-preset-sub { display: block; color: var(--rm-muted); font-size: 11px; font-weight: 400; line-height: 1.35; margin-top: 1px; }\n.rm-create-custom {\n margin-top: 14px; padding: 12px; border: 1px dashed var(--rm-border); border-radius: 12px;\n display: flex; flex-direction: column; gap: 10px;\n}\n.rm-create-custom-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }\n.rm-draft-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }\n.rm-draft-item {\n display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: 8px;\n border: 1px solid var(--rm-border); background: var(--rm-panel);\n}\n.rm-draft-main { flex: 1; min-width: 0; display: flex; flex-direction: column; font-size: 12.5px; }\n.rm-draft-main span { color: var(--rm-muted); font-size: 11px; }\n.rm-draft-form { margin-top: 4px; padding-top: 10px; border-top: 1px dashed var(--rm-border); }\n.rm-linkish { background: none; border: 0; color: var(--rm-accent, #5b8cff); cursor: pointer; font: inherit; font-size: inherit; padding: 0; text-decoration: underline; }\n@media (max-width: 720px) {\n .rm-modal-create, .rm-modal-scrollable { width: min(100vw - 24px, 520px) !important; }\n}\n';
1559
2196
 
1560
2197
  // src/client/store.js
1561
2198
  var API = "/api/collab";
@@ -1733,6 +2370,37 @@ function createRoomsStore() {
1733
2370
  },
1734
2371
  async mcp(roomId, tool, args, target) {
1735
2372
  return post(`/rooms/${encodeURIComponent(roomId)}/mcp`, { tool, args, target });
2373
+ },
2374
+ // ── local multi-agent ──
2375
+ async presets() {
2376
+ return (await call("/presets")).presets;
2377
+ },
2378
+ async addAgent(roomId, body) {
2379
+ return post(`/rooms/${encodeURIComponent(roomId)}/agents`, body);
2380
+ },
2381
+ async updateAgent(roomId, id, body) {
2382
+ return post(`/rooms/${encodeURIComponent(roomId)}/agents/${encodeURIComponent(id)}`, body);
2383
+ },
2384
+ async removeAgent(roomId, id) {
2385
+ return post(`/rooms/${encodeURIComponent(roomId)}/agents/${encodeURIComponent(id)}?op=delete`);
2386
+ },
2387
+ async saveLocal(roomId, patch) {
2388
+ return post(`/rooms/${encodeURIComponent(roomId)}/local`, patch);
2389
+ },
2390
+ async setTransport(roomId, target, force) {
2391
+ return post(`/rooms/${encodeURIComponent(roomId)}/transport`, { target, force: !!force });
2392
+ },
2393
+ async stop(roomId, agentId) {
2394
+ return post(`/rooms/${encodeURIComponent(roomId)}/stop`, agentId ? { agentId } : {});
2395
+ },
2396
+ async diffs(roomId) {
2397
+ return call(`/rooms/${encodeURIComponent(roomId)}/diffs`);
2398
+ },
2399
+ async merge(roomId, agentId) {
2400
+ return post(`/rooms/${encodeURIComponent(roomId)}/merge`, { agentId, approve: true });
2401
+ },
2402
+ async discard(roomId, agentId) {
2403
+ return post(`/rooms/${encodeURIComponent(roomId)}/discard`, { agentId, approve: true });
1736
2404
  }
1737
2405
  };
1738
2406
  const api = {
@@ -1788,6 +2456,17 @@ var LockIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { size: 1
1788
2456
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "3.5", y: "7", width: "9", height: "6.25", rx: "1.5" }),
1789
2457
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5.5 7V5.25a2.5 2.5 0 0 1 5 0V7" })
1790
2458
  ] });
2459
+ var BotIcon = ({ size = 14 }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { size, children: [
2460
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "3", y: "5", width: "10", height: "7.5", rx: "2" }),
2461
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 2.5V5M6 8.5h.01M10 8.5h.01M6.25 10.75h3.5" })
2462
+ ] });
2463
+ var StopIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Svg, { size: 12, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1.5" }) });
2464
+ var MergeIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { size: 14, children: [
2465
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "5", cy: "3.75", r: "1.5" }),
2466
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "5", cy: "12.25", r: "1.5" }),
2467
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "11", cy: "8", r: "1.5" }),
2468
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 5.25v5.5M5 5.5c0 2 1.5 2.5 4.5 2.5" })
2469
+ ] });
1791
2470
  var PeopleIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { size: 14, children: [
1792
2471
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "6", cy: "5.5", r: "2.25" }),
1793
2472
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M2.5 13c.4-2 1.8-3.25 3.5-3.25S9.1 11 9.5 13" }),
@@ -1908,6 +2587,10 @@ function RoomsNav({ store }) {
1908
2587
  }
1909
2588
  function RoomSessionMark({ store, sessionId }) {
1910
2589
  const s = useStore(store);
2590
+ for (const r of s.rooms || []) {
2591
+ const ag = (r.local && r.local.agents || []).find((x) => x.sessionId === sessionId);
2592
+ if (ag) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-session-mark rm-session-agent", title: `DSH Room \xB7 ${r.name} \xB7 ${ag.name} (${ag.agentId})`, "aria-label": `DSH Room ${r.name} agent ${ag.name}`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BotIcon, { size: 13 }) });
2593
+ }
1911
2594
  const room = (s.rooms || []).find((r) => r.sessionId === sessionId);
1912
2595
  if (!room) return null;
1913
2596
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-session-mark", title: `DSH Room \xB7 ${room.name}`, "aria-label": `DSH Room ${room.name}`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomsIcon, { size: 13 }) });
@@ -1933,14 +2616,14 @@ function renderMentions(text) {
1933
2616
  function PanelIcon({ size }) {
1934
2617
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomsIcon, { size });
1935
2618
  }
1936
- function Message({ m, showAuthor }) {
2619
+ function Message({ m, showAuthor, who }) {
1937
2620
  if (m.kind === "task.create") {
1938
2621
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-msg rm-msg-event", "data-message-id": m.id, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-task-card", children: [
1939
2622
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-task-head", children: [
1940
2623
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TaskIcon, {}),
1941
2624
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
1942
2625
  "Task \xB7 ",
1943
- m.from
2626
+ who && who.name || m.from
1944
2627
  ] }),
1945
2628
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "info", children: "open" }),
1946
2629
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-msg-time", children: fmtTime(m.ts) })
@@ -1953,7 +2636,7 @@ function Message({ m, showAuthor }) {
1953
2636
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-msg rm-msg-event", "data-message-id": m.id, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-task-head", children: [
1954
2637
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TaskIcon, {}),
1955
2638
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
1956
- m.self ? "You" : m.from,
2639
+ m.self ? "You" : who && who.name || m.from,
1957
2640
  " ",
1958
2641
  m.kind === "task.claim" ? "claimed" : "updated",
1959
2642
  " task"
@@ -1973,14 +2656,20 @@ function Message({ m, showAuthor }) {
1973
2656
  showAuthor ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: m.from, size: 28 }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {}),
1974
2657
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1975
2658
  showAuthor ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-msg-head", children: [
1976
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-msg-author", children: m.from }),
2659
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-msg-author", children: who && who.name ? who.name : m.from }),
2660
+ who && who.name ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-msg-id", children: m.from }) : null,
2661
+ who && who.local ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-tag", children: [
2662
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BotIcon, { size: 11 }),
2663
+ who.moderator ? "moderator" : "local agent"
2664
+ ] }) : null,
1977
2665
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-msg-time", children: fmtTime(m.ts) })
1978
2666
  ] }) : null,
1979
2667
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-msg-body", children: renderMentions(m.text) })
1980
2668
  ] })
1981
2669
  ] });
1982
2670
  }
1983
- function ChatBody({ messages }) {
2671
+ function ChatBody({ messages, members }) {
2672
+ const byId = (0, import_react.useMemo)(() => Object.fromEntries((members || []).map((x) => [x.agentId, x])), [members]);
1984
2673
  const ref = (0, import_react.useRef)(null);
1985
2674
  (0, import_react.useEffect)(() => {
1986
2675
  const el = ref.current;
@@ -1996,7 +2685,7 @@ function ChatBody({ messages }) {
1996
2685
  prev = null;
1997
2686
  }
1998
2687
  const showAuthor = !prev || prev.from !== m.from || m.ts - prev.ts > 5 * 6e4;
1999
- nodes.push(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Message, { m, showAuthor }, m.id));
2688
+ nodes.push(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Message, { m, showAuthor, who: byId[m.from] }, m.id));
2000
2689
  prev = m;
2001
2690
  }
2002
2691
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-messages", ref, children: nodes.length ? nodes : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-chat-hint", children: "No messages yet. Send a message, @mention an agent to wake it, or create a task from the composer." }) });
@@ -2023,7 +2712,7 @@ function MentionMenu({ items, active, onPick, onHover }) {
2023
2712
  onPick(it);
2024
2713
  },
2025
2714
  children: [
2026
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: it.id === "all" ? "all" : it.id, size: 22 }),
2715
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: it.id === "all" ? "all" : it.name || it.id, size: 22 }),
2027
2716
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-mention-item-body", children: [
2028
2717
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-mention-item-name", children: [
2029
2718
  "@",
@@ -2031,6 +2720,10 @@ function MentionMenu({ items, active, onPick, onHover }) {
2031
2720
  ] }),
2032
2721
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-mention-item-meta", children: it.hint })
2033
2722
  ] }),
2723
+ it.local ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-tag", children: [
2724
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BotIcon, { size: 11 }),
2725
+ "local"
2726
+ ] }) : null,
2034
2727
  it.online === false ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "quiet", children: "offline" }) : null
2035
2728
  ]
2036
2729
  },
@@ -2055,14 +2748,20 @@ function Composer({ room, store, draft, setDraft }) {
2055
2748
  if (!mq) return [];
2056
2749
  const q = mq.query.toLowerCase();
2057
2750
  const members = (room.members || []).filter((m) => !m.self);
2058
- const known = members.map((m) => ({
2059
- id: m.agentId,
2060
- hint: m.online === false ? "member \xB7 offline" : "member \xB7 wake",
2061
- online: m.online !== false
2062
- }));
2063
- const all = { id: "all", hint: "Everyone \xB7 wake all members", online: true };
2751
+ const localAgents = room.local && room.local.agents || [];
2752
+ const seen = /* @__PURE__ */ new Set();
2753
+ const known = [];
2754
+ for (const la of localAgents) {
2755
+ seen.add(la.agentId);
2756
+ known.push({ id: la.agentId, name: la.name, local: true, hint: `${la.name} \xB7 local agent${la.moderator ? " \xB7 moderator" : ""}${la.state && la.state !== "idle" ? ` \xB7 ${la.state}` : ""}`, online: la.online !== false });
2757
+ }
2758
+ for (const m of members) {
2759
+ if (seen.has(m.agentId)) continue;
2760
+ known.push({ id: m.agentId, name: m.name, hint: `${m.name ? m.name + " \xB7 " : ""}${m.hostedBy ? "remote agent" : "member"}${m.online === false ? " \xB7 offline" : " \xB7 wake"}`, online: m.online !== false });
2761
+ }
2762
+ const all = { id: "all", hint: localAgents.length ? "Everyone \xB7 wakes all local agents in parallel" : "Everyone \xB7 wake all members", online: true };
2064
2763
  const list = [all, ...known];
2065
- return list.filter((it) => !q || it.id.toLowerCase().startsWith(q) || ("@" + it.id).toLowerCase().startsWith("@" + q));
2764
+ return list.filter((it) => !q || it.id.toLowerCase().startsWith(q) || it.name && it.name.toLowerCase().startsWith(q));
2066
2765
  })();
2067
2766
  const mc = room.memberConfig || {};
2068
2767
  const modelLabel = mc.model || "Default model";
@@ -2100,6 +2799,23 @@ function Composer({ room, store, draft, setDraft }) {
2100
2799
  a.toast(e.message);
2101
2800
  }
2102
2801
  };
2802
+ const syncCaret = (el) => {
2803
+ if (el) setCaret(el.selectionStart == null ? el.value.length : el.selectionStart);
2804
+ };
2805
+ const insertMention = (it) => {
2806
+ if (!mq || !it) return;
2807
+ const next = draft.slice(0, mq.start) + "@" + it.id + " " + draft.slice(mq.end);
2808
+ const pos = mq.start + it.id.length + 2;
2809
+ setDraft(next);
2810
+ setCaret(pos);
2811
+ requestAnimationFrame(() => {
2812
+ const el = taRef.current;
2813
+ if (el) {
2814
+ el.focus();
2815
+ el.setSelectionRange(pos, pos);
2816
+ }
2817
+ });
2818
+ };
2103
2819
  const submit = async () => {
2104
2820
  const text = draft.trim();
2105
2821
  if (!text || busy) return;
@@ -2296,7 +3012,15 @@ function RoomDetail({ room, messages, store, mention, assign }) {
2296
3012
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: m.agentId, size: 26 }),
2297
3013
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-dot rm-dot-" + (m.online === false ? "offline" : "online"), style: { position: "absolute", right: -1, bottom: -1, border: "2px solid var(--rm-bg)" } })
2298
3014
  ] }),
2299
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-member-name", children: m.agentId }),
3015
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-member-name", children: m.name ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
3016
+ m.name,
3017
+ " ",
3018
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-msg-id", children: m.agentId })
3019
+ ] }) : m.agentId }),
3020
+ m.local ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-tag", children: [
3021
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BotIcon, { size: 11 }),
3022
+ m.moderator ? "moderator" : "local"
3023
+ ] }) : m.hostedBy && !m.self ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-agent-tag", children: "remote agent" }) : null,
2300
3024
  m.self ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "quiet", children: "you" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-member-actions", children: [
2301
3025
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mini", title: `@mention ${m.agentId}`, onClick: () => mention(m.agentId), children: "@" }),
2302
3026
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mini", title: `Assign a task to ${m.agentId}`, onClick: () => assign(m.agentId), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TaskIcon, {}) })
@@ -2304,6 +3028,9 @@ function RoomDetail({ room, messages, store, mention, assign }) {
2304
3028
  ] }, m.agentId)),
2305
3029
  !room.members.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: room.status === "online" ? "Only you are here." : "Offline." }) : null
2306
3030
  ] }),
3031
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LocalAgentsSection, { room, store, mention }),
3032
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OrchestrationSection, { room, store }),
3033
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IsolationSection, { room, store }),
2307
3034
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", children: [
2308
3035
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-h", children: [
2309
3036
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TaskIcon, {}),
@@ -2326,8 +3053,10 @@ function RoomDetail({ room, messages, store, mention, assign }) {
2326
3053
  "Invite"
2327
3054
  ] }),
2328
3055
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: room.public ? "Public: listed in the relay directory with its key. Anyone can find, join, and read this room." : "Private: only agents holding the invite can join. The invite contains the room key \u2014 share it only with agents you trust." }),
3056
+ room.transport === "local" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "Local-only room: switch Transport to Relay or LAN before inviting remote hosts." }) : null,
2329
3057
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-actions", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", onClick: copyInvite, icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LinkIcon, {}), children: "Copy invite" }) })
2330
3058
  ] }),
3059
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TransportSection, { room, store }),
2331
3060
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", children: [
2332
3061
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-detail-h", children: "About" }),
2333
3062
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("dl", { className: "rm-about", children: [
@@ -2340,7 +3069,7 @@ function RoomDetail({ room, messages, store, mention, assign }) {
2340
3069
  " Private"
2341
3070
  ] }) }),
2342
3071
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", { children: "Transport" }),
2343
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: room.transport === "lan" ? "LAN / VPS (Node relay)" : "Relay (Cloudflare)" }),
3072
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: room.transport === "local" ? "Local only (in-process loopback)" : room.transport === "lan" ? "LAN / VPS (Node relay)" : "Relay (Cloudflare)" }),
2344
3073
  room.memberConfig?.workspaceLabel ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2345
3074
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", { children: "Workspace" }),
2346
3075
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: room.memberConfig.workspaceLabel })
@@ -2367,6 +3096,391 @@ function RoomDetail({ room, messages, store, mention, assign }) {
2367
3096
  ] })
2368
3097
  ] });
2369
3098
  }
3099
+ var RUN_TONE = { running: "warning", queued: "info", approval: "info", done: "success", failed: "danger", stopped: "quiet" };
3100
+ var MODEL_CHOICES = ["", "deepseek-chat", "deepseek-reasoner"];
3101
+ function LocalAgentsSection({ room, store, mention }) {
3102
+ const a = store.actions;
3103
+ const local = room.local || { agents: [] };
3104
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", "data-rm-local-agents": "", children: [
3105
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-h", children: [
3106
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BotIcon, {}),
3107
+ "Local agents \xB7 ",
3108
+ local.agents.length
3109
+ ] }),
3110
+ local.agents.map((ag) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-agent-row", "data-agent-id": ag.agentId, children: [
3111
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-avatar-wrap", children: [
3112
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: ag.name, size: 26 }),
3113
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-dot rm-dot-" + (ag.state === "running" ? "busy" : ag.online ? "online" : "offline"), style: { position: "absolute", right: -1, bottom: -1, border: "2px solid var(--rm-bg)" } })
3114
+ ] }),
3115
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-main", children: [
3116
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-name", children: [
3117
+ ag.name,
3118
+ ag.moderator ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-agent-tag", children: "moderator" }) : null
3119
+ ] }),
3120
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-sub rm-mono", children: [
3121
+ "@",
3122
+ ag.agentId
3123
+ ] }),
3124
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-sub", children: [
3125
+ ag.scope === "write" ? ag.isolation === "worktree" ? "writes \xB7 own worktree" : ag.isolation === "locks" ? "writes \xB7 file locks" : "writes" : "read-only",
3126
+ ag.approvalRequired ? " \xB7 approval" : "",
3127
+ ag.model ? ` \xB7 ${ag.model}` : "",
3128
+ ag.reasoningEffort ? ` \xB7 ${ag.reasoningEffort}` : ""
3129
+ ] })
3130
+ ] }),
3131
+ ag.state !== "idle" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: RUN_TONE[ag.state] || "neutral", children: ag.state }) : null,
3132
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-member-actions", children: [
3133
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mini", title: `@mention ${ag.name}`, onClick: () => mention(ag.agentId), children: "@" }),
3134
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mini", title: `Edit ${ag.name}`, "aria-label": `Edit ${ag.name}`, onClick: () => a.openDialog({ kind: "agent", roomId: room.roomId, agentId: ag.agentId }), children: "\u270E" })
3135
+ ] })
3136
+ ] }, ag.agentId)),
3137
+ !local.agents.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "Add a custom agent (free-form name, prompt, model, scope) or start from a preset. Each gets its own identity, session and scope." }) : null,
3138
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-actions", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {}), onClick: () => a.openDialog({ kind: "agent", roomId: room.roomId }), children: "Add agent" }) })
3139
+ ] });
3140
+ }
3141
+ function NumberSetting({ label, value, min, max, onSave, hint }) {
3142
+ const [v, setV] = (0, import_react.useState)(String(value));
3143
+ (0, import_react.useEffect)(() => setV(String(value)), [value]);
3144
+ const commit = () => {
3145
+ const n = Number(v);
3146
+ if (Number.isFinite(n) && n !== value) onSave(Math.min(max, Math.max(min, Math.round(n))));
3147
+ else setV(String(value));
3148
+ };
3149
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "rm-num-setting", title: hint, children: [
3150
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: label }),
3151
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "number", min, max, value: v, "aria-label": label, onChange: (e) => setV(e.target.value), onBlur: commit, onKeyDown: (e) => e.key === "Enter" && e.target.blur() })
3152
+ ] });
3153
+ }
3154
+ function RunQueue({ room, store, compact }) {
3155
+ const a = store.actions;
3156
+ const runs = room.runs || [];
3157
+ const nameOf = (id) => (room.local && room.local.agents || []).find((x) => x.agentId === id)?.name || id;
3158
+ const active = runs.filter((r) => r.state === "running" || r.state === "queued" || r.state === "approval");
3159
+ const recent = runs.filter((r) => !active.includes(r)).slice(-3).reverse();
3160
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-runs", "data-rm-runs": "", children: [
3161
+ active.map((r, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-run rm-run-" + r.state, children: [
3162
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-run-pos", children: r.state === "running" ? "\u25B6" : r.state === "approval" ? "?" : `#${i + 1 - active.filter((x) => x.state === "running").length}` }),
3163
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-run-who", children: nameOf(r.agentId) }),
3164
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-run-what", title: r.summary, children: r.summary }),
3165
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: RUN_TONE[r.state], children: r.state === "approval" ? "needs approval" : r.state })
3166
+ ] }, r.id)),
3167
+ !compact ? recent.map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-run rm-run-" + r.state, children: [
3168
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-run-pos", children: "\u2713" }),
3169
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-run-who", children: nameOf(r.agentId) }),
3170
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-run-what", title: r.error || r.summary, children: r.error || r.summary }),
3171
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: RUN_TONE[r.state], children: r.state })
3172
+ ] }, r.id)) : null,
3173
+ !active.length && !recent.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "No agent runs yet." }) : null,
3174
+ active.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-actions", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", className: "rm-danger", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StopIcon, {}), onClick: () => a.stop(room.roomId).then((r) => a.toast(`Stopped ${r.stopped} run(s)`)).catch((e) => a.toast(e.message)), children: "Stop all agents" }) }) : null
3175
+ ] });
3176
+ }
3177
+ function OrchestrationSection({ room, store }) {
3178
+ const a = store.actions;
3179
+ const local = room.local;
3180
+ if (!local || !local.agents.length) return null;
3181
+ const save = (patch) => a.saveLocal(room.roomId, patch).catch((e) => a.toast(e.message));
3182
+ const active = (room.runs || []).filter((r) => r.state === "running" || r.state === "queued" || r.state === "approval");
3183
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", "data-rm-orchestration": "", children: [
3184
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-h", children: [
3185
+ "Runs \xB7 ",
3186
+ active.filter((r) => r.state === "running").length,
3187
+ "/",
3188
+ local.concurrency,
3189
+ " running",
3190
+ active.length > local.concurrency ? ` \xB7 ${active.length - Math.min(local.concurrency, active.filter((r) => r.state === "running").length)} queued` : ""
3191
+ ] }),
3192
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-num-row", children: [
3193
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NumberSetting, { label: "Parallel runs", value: local.concurrency, min: 1, max: 16, onSave: (n) => save({ concurrency: n }), hint: "Max local agent runs at once; the rest wait in the queue." }),
3194
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NumberSetting, { label: "Turn limit", value: local.turnLimit, min: 1, max: 100, onSave: (n) => save({ turnLimit: n }), hint: "Max agent-to-agent hand-offs per task/thread. A human message resets it." })
3195
+ ] }),
3196
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RunQueue, { room, store })
3197
+ ] });
3198
+ }
3199
+ function IsolationSection({ room, store }) {
3200
+ const a = store.actions;
3201
+ const local = room.local;
3202
+ if (!local || !local.agents.some((x) => x.scope === "write")) return null;
3203
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", "data-rm-isolation": "", children: [
3204
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-h", children: [
3205
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MergeIcon, {}),
3206
+ "Workspace isolation"
3207
+ ] }),
3208
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { className: "rm-input", "aria-label": "Isolation mode", value: local.isolation, onChange: (e) => a.saveLocal(room.roomId, { isolation: e.target.value }).catch((x) => a.toast(x.message)), children: [
3209
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "auto", children: "Auto \u2014 git worktree per writing agent, else file locks" }),
3210
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "worktree", children: "Git worktree per agent" }),
3211
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "locks", children: "File locks (shared workspace)" }),
3212
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "off", children: "Off (shared, no locks)" })
3213
+ ] }),
3214
+ local.locks && local.locks.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-note", children: [
3215
+ local.locks.length,
3216
+ " file lock(s): ",
3217
+ local.locks.slice(0, 4).map((l) => `${l.path} (${l.owner})`).join(", ")
3218
+ ] }) : null,
3219
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-actions", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MergeIcon, {}), onClick: () => a.openDialog({ kind: "merge", roomId: room.roomId }), children: "Review & merge" }) })
3220
+ ] });
3221
+ }
3222
+ function TransportSection({ room, store }) {
3223
+ const a = store.actions;
3224
+ const [busy, setBusy] = (0, import_react.useState)(null);
3225
+ const cur = room.transport === "local" ? "local" : room.transport === "lan" ? "relay" : "relay";
3226
+ const go = async (target) => {
3227
+ setBusy(target);
3228
+ try {
3229
+ await a.setTransport(room.roomId, target);
3230
+ a.toast(target === "local" ? "Local only \u2014 in-process, no network" : target === "lan" ? "Now on this machine's LAN relay \u2014 invite remote hosts" : "Now on the relay \u2014 invite remote hosts");
3231
+ } catch (e) {
3232
+ a.toast(e.message);
3233
+ } finally {
3234
+ setBusy(null);
3235
+ }
3236
+ };
3237
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", "data-rm-transport": "", children: [
3238
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-detail-h", children: "Transport" }),
3239
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-mode-row", children: [
3240
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", disabled: !!busy || room.public, className: "rm-mode-btn" + (cur === "local" ? " is-on" : ""), onClick: () => go("local"), title: room.public ? "Public rooms need a relay" : "In-process loopback: no network", children: busy === "local" ? "\u2026" : "Local only" }),
3241
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", disabled: !!busy, className: "rm-mode-btn" + (cur === "relay" && room.transport !== "local" ? " is-on" : ""), onClick: () => go("relay"), children: busy === "relay" ? "\u2026" : "Relay (VPS)" }),
3242
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", disabled: !!busy, className: "rm-mode-btn", onClick: () => go("lan"), title: "Embedded LAN relay on this machine (mDNS)", children: busy === "lan" ? "\u2026" : "LAN" })
3243
+ ] }),
3244
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: room.transport === "local" ? "All members are on this dsh \u2014 nothing leaves the process. Switch to Relay or LAN to invite remote hosts; local agents stay members." : "Local agents connect as separate members over this transport, alongside remote hosts." })
3245
+ ] });
3246
+ }
3247
+ function AgentDialog({ store, d }) {
3248
+ const a = store.actions;
3249
+ const s = useStore(store);
3250
+ const room = (s.rooms || []).find((r) => r.roomId === d.roomId);
3251
+ const existing = room && room.local ? room.local.agents.find((x) => x.agentId === d.agentId) : null;
3252
+ const [presets, setPresets] = (0, import_react.useState)([]);
3253
+ const blank = () => ({ preset: "", name: "", persona: "", systemPrompt: "", model: "", reasoningEffort: "medium", scope: "read", approvalRequired: false, moderator: false });
3254
+ const [f, setF] = (0, import_react.useState)(() => existing ? { preset: "", name: existing.name, persona: existing.persona || "", systemPrompt: existing.systemPrompt || "", model: existing.model || "", reasoningEffort: existing.reasoningEffort || "medium", scope: existing.scope, approvalRequired: !!existing.approvalRequired, moderator: !!existing.moderator } : blank());
3255
+ const [err, setErr] = (0, import_react.useState)(null);
3256
+ const [busy, setBusy] = (0, import_react.useState)(false);
3257
+ (0, import_react.useEffect)(() => {
3258
+ a.presets().then(setPresets).catch(() => {
3259
+ });
3260
+ }, []);
3261
+ const pick = (p) => setF({ ...f, preset: p.key, name: p.name, persona: p.persona || "", systemPrompt: p.systemPrompt || "", scope: p.scope, moderator: !!p.moderator, reasoningEffort: p.reasoningEffort || "medium" });
3262
+ const clearPreset = () => setF({ ...f, preset: "" });
3263
+ const submit = async () => {
3264
+ if (!f.name.trim()) {
3265
+ setErr("Name required");
3266
+ return;
3267
+ }
3268
+ setBusy(true);
3269
+ setErr(null);
3270
+ try {
3271
+ const body = { name: f.name.trim(), persona: f.persona, systemPrompt: f.systemPrompt, model: f.model || void 0, reasoningEffort: f.reasoningEffort, scope: f.scope, approvalRequired: f.approvalRequired, moderator: f.moderator };
3272
+ if (existing) await a.updateAgent(d.roomId, existing.agentId, body);
3273
+ else {
3274
+ const r = await a.addAgent(d.roomId, f.preset ? { ...body, preset: f.preset } : body);
3275
+ a.toast(`${r.name} joined as @${r.agentId}`);
3276
+ }
3277
+ a.closeDialog();
3278
+ } catch (e) {
3279
+ setErr(e.message);
3280
+ setBusy(false);
3281
+ }
3282
+ };
3283
+ const remove = async () => {
3284
+ setBusy(true);
3285
+ try {
3286
+ const r = await a.removeAgent(d.roomId, existing.agentId);
3287
+ a.toast(`Removed ${existing.name}${r.branchKept ? ` \xB7 branch ${r.branchKept} kept` : ""}`);
3288
+ a.closeDialog();
3289
+ } catch (e) {
3290
+ setErr(e.message);
3291
+ setBusy(false);
3292
+ }
3293
+ };
3294
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3295
+ import_dsh_client_ui_primitives.Modal,
3296
+ {
3297
+ open: true,
3298
+ onClose: a.closeDialog,
3299
+ title: existing ? `Edit ${existing.name}` : "Add local agent",
3300
+ closeLabel: "Close",
3301
+ className: "rm-modal-wide rm-modal-scrollable",
3302
+ contentClassName: "rm-modal-scroll",
3303
+ description: existing ? `Member @${existing.agentId} \xB7 own identity and dsh session in this room's workspace.` : "Free-form agent: own name, persona, prompt, model, scope. Presets only fill the form \u2014 you can edit every field. Remote peers see it as a separate member.",
3304
+ footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
3305
+ existing ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", className: "rm-danger", disabled: busy, onClick: remove, children: "Remove" }) : null,
3306
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { flex: 1 } }),
3307
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Cancel" }),
3308
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", disabled: busy, onClick: submit, children: busy ? "Saving\u2026" : existing ? "Save" : "Add agent" })
3309
+ ] }),
3310
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-form rm-form-wide", "data-rm-agent-form": "", children: [
3311
+ !existing ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-span-2", children: [
3312
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Start from" }),
3313
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "Presets fill the fields below; switch to Custom anytime and edit freely." }),
3314
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-preset-row", children: [
3315
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-preset" + (!f.preset ? " is-on" : ""), onClick: () => setF(blank()), children: [
3316
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "Custom" }),
3317
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Blank form \u2014 your name, prompt, model" })
3318
+ ] }),
3319
+ presets.map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-preset" + (f.preset === p.key ? " is-on" : ""), onClick: () => pick(p), children: [
3320
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: p.name }),
3321
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: p.persona })
3322
+ ] }, p.key))
3323
+ ] })
3324
+ ] }) : null,
3325
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Name", htmlFor: "rm-ag-name", hint: "Member id is derived from it (e.g. coder.ab12).", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-ag-name", value: f.name, maxLength: 40, onChange: (e) => {
3326
+ const name2 = e.target.value;
3327
+ const presetName = (presets.find((p) => p.key === f.preset) || {}).name;
3328
+ setF({ ...f, name: name2, preset: f.preset && name2 !== presetName ? "" : f.preset });
3329
+ }, placeholder: "e.g. Data wrangler" }) }),
3330
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Persona", htmlFor: "rm-ag-persona", hint: "One line other members see.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-ag-persona", value: f.persona, maxLength: 200, onChange: (e) => setF({ ...f, persona: e.target.value, ...f.preset ? { preset: "" } : {} }), placeholder: "e.g. Cleans CSVs and writes summaries" }) }),
3331
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "System prompt", htmlFor: "rm-ag-sp", hint: "Prepended to every wake of this agent.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("textarea", { id: "rm-ag-sp", className: "rm-input rm-textarea", rows: 4, value: f.systemPrompt, maxLength: 4e3, onChange: (e) => setF({ ...f, systemPrompt: e.target.value, ...f.preset ? { preset: "" } : {} }), placeholder: "You are \u2026 Focus on \u2026 Report back with \u2026" }) }) }),
3332
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Model", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { className: "rm-input", "aria-label": "Model", value: f.model, onChange: (e) => setF({ ...f, model: e.target.value }), children: [
3333
+ MODEL_CHOICES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: m, children: m || "dsh default model" }, m)),
3334
+ f.model && !MODEL_CHOICES.includes(f.model) ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: f.model, children: f.model }) : null
3335
+ ] }) }),
3336
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Reasoning effort", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { className: "rm-input", "aria-label": "Reasoning effort", value: f.reasoningEffort, onChange: (e) => setF({ ...f, reasoningEffort: e.target.value }), children: ["low", "medium", "high", "max"].map((x) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: x, children: x }, x)) }) }),
3337
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Workspace scope", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-mode-row", children: [
3338
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (f.scope === "read" ? " is-on" : ""), onClick: () => setF({ ...f, scope: "read" }), children: "Read-only" }),
3339
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (f.scope === "write" ? " is-on" : ""), onClick: () => setF({ ...f, scope: "write" }), children: "Read + write" })
3340
+ ] }) }),
3341
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-col", children: [
3342
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-row", children: [
3343
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
3344
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Approval required" }),
3345
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "Each run waits for your OK." })
3346
+ ] }),
3347
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: f.approvalRequired, onChange: (v) => setF({ ...f, approvalRequired: v }), label: "Approval required" })
3348
+ ] }),
3349
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-row", children: [
3350
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
3351
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Moderator" }),
3352
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "Splits requests into tasks and summarizes. One per room." })
3353
+ ] }),
3354
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: f.moderator, onChange: (v) => setF({ ...f, moderator: v }), label: "Moderator" })
3355
+ ] })
3356
+ ] }),
3357
+ f.scope === "write" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2 rm-note", children: "Writes go to its own git worktree (branch room/<room>/<agent>) when the workspace is a git repo; you merge from \u201CReview & merge\u201D. Non-git workspaces use file locks." }) : null,
3358
+ f.preset ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-span-2 rm-note", children: [
3359
+ "Filled from the ",
3360
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: f.preset }),
3361
+ " preset \u2014 editing any field switches to Custom. ",
3362
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-linkish", onClick: clearPreset, children: "Clear preset tag" })
3363
+ ] }) : null,
3364
+ err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2 rm-error", children: err }) : null
3365
+ ] })
3366
+ }
3367
+ );
3368
+ }
3369
+ function DiffView({ patch }) {
3370
+ if (!patch) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "No changes." });
3371
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { className: "rm-diff", "data-rm-diff": "", children: patch.split("\n").map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: l.startsWith("+++") || l.startsWith("---") ? "rm-diff-file" : l.startsWith("+") ? "rm-diff-add" : l.startsWith("-") ? "rm-diff-del" : l.startsWith("@@") ? "rm-diff-hunk" : l.startsWith("diff ") ? "rm-diff-head" : "", children: l || " " }, i)) });
3372
+ }
3373
+ function MergeDialog({ store, d }) {
3374
+ const a = store.actions;
3375
+ const [data, setData] = (0, import_react.useState)(null);
3376
+ const [sel, setSel] = (0, import_react.useState)(null);
3377
+ const [confirm, setConfirm] = (0, import_react.useState)(null);
3378
+ const [busy, setBusy] = (0, import_react.useState)(false);
3379
+ const [result, setResult] = (0, import_react.useState)(null);
3380
+ const load = async () => {
3381
+ try {
3382
+ const r = await a.diffs(d.roomId);
3383
+ setData(r);
3384
+ setSel((cur) => cur && r.agents.some((x) => x.agentId === cur) ? cur : (r.agents.find((x) => x.ahead) || r.agents[0] || {}).agentId || null);
3385
+ } catch (e) {
3386
+ setData({ error: e.message, agents: [] });
3387
+ }
3388
+ };
3389
+ (0, import_react.useEffect)(() => {
3390
+ void load();
3391
+ }, [d.roomId]);
3392
+ const ag = data && data.agents.find((x) => x.agentId === sel);
3393
+ const run = async (kind) => {
3394
+ setBusy(true);
3395
+ setResult(null);
3396
+ try {
3397
+ const r = kind === "merge" ? await a.merge(d.roomId, sel) : await a.discard(d.roomId, sel);
3398
+ setResult(kind === "merge" ? r.merged ? { ok: true, text: `Merged into the main workspace (${r.commit}).` } : { ok: false, text: `${r.error || "Not merged"}${r.conflicts && r.conflicts.length ? ": " + r.conflicts.join(", ") : ""}` } : { ok: true, text: r.discarded ? `Discarded ${r.branch}.` : "Nothing to discard." });
3399
+ await load();
3400
+ } catch (e) {
3401
+ setResult({ ok: false, text: e.message });
3402
+ } finally {
3403
+ setBusy(false);
3404
+ setConfirm(null);
3405
+ }
3406
+ };
3407
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3408
+ import_dsh_client_ui_primitives.Modal,
3409
+ {
3410
+ open: true,
3411
+ onClose: a.closeDialog,
3412
+ title: "Review & merge agent changes",
3413
+ closeLabel: "Close",
3414
+ className: "rm-modal-wide rm-modal-xl",
3415
+ description: "Each writing agent works on its own branch. Review the diff, then merge into (or discard from) your main workspace. Nothing is merged without your confirmation.",
3416
+ footer: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Close" }),
3417
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-root rm-merge", "data-rm-merge": "", children: !data ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "Loading diffs\u2026" }) : data.error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-error", children: data.error }) : !data.agents.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "No writing agents in this room." }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
3418
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-merge-list", children: data.agents.map((x) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-merge-agent" + (x.agentId === sel ? " is-on" : ""), onClick: () => {
3419
+ setSel(x.agentId);
3420
+ setConfirm(null);
3421
+ setResult(null);
3422
+ }, children: [
3423
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: x.name, size: 24 }),
3424
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-merge-agent-body", children: [
3425
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: x.name }),
3426
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-mono", children: x.branch || (x.isolation === "locks" ? "shared workspace \xB7 locks" : "no worktree yet") })
3427
+ ] }),
3428
+ x.conflicts && x.conflicts.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "danger", children: "conflict" }) : x.ahead ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_dsh_client_ui_primitives.Tag, { tone: "info", children: [
3429
+ x.files.length,
3430
+ " file",
3431
+ x.files.length === 1 ? "" : "s"
3432
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "quiet", children: "clean" })
3433
+ ] }, x.agentId)) }),
3434
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-merge-main", children: ag ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
3435
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-merge-head", children: [
3436
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
3437
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: ag.name }),
3438
+ " ",
3439
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-mono", children: ag.branch || "" }),
3440
+ ag.ahead ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-note", style: { margin: "0 0 0 8px" }, children: [
3441
+ ag.ahead,
3442
+ " commit(s) ahead"
3443
+ ] }) : null
3444
+ ] }),
3445
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-actions", style: { margin: 0 }, children: confirm ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
3446
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-confirm-text", children: confirm === "merge" ? `Merge ${ag.files.length} file(s) into the main workspace?` : `Discard ${ag.name}'s branch and worktree?` }),
3447
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", onClick: () => setConfirm(null), children: "Cancel" }),
3448
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "primary", disabled: busy, onClick: () => run(confirm), children: busy ? "Working\u2026" : confirm === "merge" ? "Confirm merge" : "Confirm discard" })
3449
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
3450
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "ghost", className: "rm-danger", disabled: !ag.branch || busy, onClick: () => setConfirm("discard"), children: "Discard" }),
3451
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "primary", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MergeIcon, {}), disabled: !ag.ahead || busy, onClick: () => setConfirm("merge"), children: "Merge into main" })
3452
+ ] }) })
3453
+ ] }),
3454
+ result ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: result.ok ? "rm-ok" : "rm-error", children: result.text }) : null,
3455
+ ag.conflicts && ag.conflicts.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-error", children: [
3456
+ "Would conflict with the main workspace: ",
3457
+ ag.conflicts.join(", "),
3458
+ " \u2014 resolve on the branch or discard."
3459
+ ] }) : null,
3460
+ ag.mainDirty && ag.mainDirty.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-error", children: [
3461
+ "Uncommitted changes in the main workspace touch: ",
3462
+ ag.mainDirty.join(", ")
3463
+ ] }) : null,
3464
+ ag.files && ag.files.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-merge-files", children: ag.files.map((f) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-merge-file", children: [
3465
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-mono", children: f.path }),
3466
+ " ",
3467
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-diff-addn", children: [
3468
+ "+",
3469
+ f.adds
3470
+ ] }),
3471
+ " ",
3472
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-diff-deln", children: [
3473
+ "\u2212",
3474
+ f.dels
3475
+ ] })
3476
+ ] }, f.path)) }) : null,
3477
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DiffView, { patch: ag.patch }),
3478
+ ag.truncated ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "Diff truncated." }) : null
3479
+ ] }) : null })
3480
+ ] }) })
3481
+ }
3482
+ );
3483
+ }
2370
3484
  function EmptyMain({ store }) {
2371
3485
  const a = store.actions;
2372
3486
  const workspaces = useNativeWorkspaces(store);
@@ -2426,11 +3540,25 @@ function RoomsMain({ store }) {
2426
3540
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-chat-header-title", children: room.name }),
2427
3541
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-chat-header-sub", children: [
2428
3542
  room.members.filter((m) => m.online !== false).length,
2429
- " online \xB7 ",
3543
+ " online",
3544
+ room.local && room.local.agents.length ? ` \xB7 ${room.local.agents.length} local agent${room.local.agents.length === 1 ? "" : "s"}` : "",
3545
+ " \xB7 ",
2430
3546
  room.public ? "public" : "private",
2431
3547
  room.status !== "online" ? ` \xB7 room ${room.status}` : ""
2432
3548
  ] })
2433
3549
  ] }),
3550
+ (() => {
3551
+ const act = (room.runs || []).filter((r) => r.state === "running" || r.state === "queued" || r.state === "approval");
3552
+ if (!act.length) return null;
3553
+ const running = act.filter((r) => r.state === "running").length;
3554
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-stop-btn", "data-rm-stop": "", title: "Stop all local agent runs", onClick: () => store.actions.stop(room.roomId).then((r) => store.actions.toast(`Stopped ${r.stopped} run(s)`)).catch((e) => store.actions.toast(e.message)), children: [
3555
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StopIcon, {}),
3556
+ "Stop \xB7 ",
3557
+ running,
3558
+ " running",
3559
+ act.length > running ? `, ${act.length - running} queued` : ""
3560
+ ] });
3561
+ })(),
2434
3562
  room.public ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-badge rm-badge-public", children: [
2435
3563
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GlobeIcon, {}),
2436
3564
  "Public"
@@ -2438,10 +3566,10 @@ function RoomsMain({ store }) {
2438
3566
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LockIcon, {}),
2439
3567
  "Private"
2440
3568
  ] }),
2441
- room.transport === "lan" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-badge", children: "LAN/VPS" }) : null,
3569
+ room.transport === "local" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-badge rm-badge-local", children: "Local only" }) : room.transport === "lan" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-badge", children: "LAN/VPS" }) : null,
2442
3570
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.StateDot, { state: room.status === "online" ? "done" : room.status === "connecting" ? "ongoing" : "error", size: 8 })
2443
3571
  ] }),
2444
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChatBody, { messages }),
3572
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChatBody, { messages, members: room.members }),
2445
3573
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Composer, { room, store, draft, setDraft })
2446
3574
  ] }),
2447
3575
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -2471,30 +3599,83 @@ function Field({ label, hint, children, htmlFor }) {
2471
3599
  function CreateDialog({ store, d }) {
2472
3600
  const s = useStore(store);
2473
3601
  const a = store.actions;
2474
- const [f, setF] = (0, import_react.useState)({ name: "", description: "", tags: "", public: false, mode: "lan", lanHostPort: "62.234.113.56:80" });
3602
+ const [tab, setTab] = (0, import_react.useState)("room");
3603
+ const [f, setF] = (0, import_react.useState)({
3604
+ name: "",
3605
+ description: "",
3606
+ tags: "",
3607
+ public: false,
3608
+ transport: "relay",
3609
+ // local | relay | lan | cloudflare
3610
+ lanHostPort: "62.234.113.56:80",
3611
+ turnLimit: 6,
3612
+ concurrency: 2,
3613
+ isolation: "auto",
3614
+ agents: []
3615
+ // draft agents: { key?, name, persona, systemPrompt, scope, moderator, reasoningEffort, approvalRequired, model }
3616
+ });
3617
+ const [draft, setDraft] = (0, import_react.useState)({ name: "", persona: "", systemPrompt: "", scope: "read", moderator: false, reasoningEffort: "medium", approvalRequired: false, model: "" });
3618
+ const [showCustom, setShowCustom] = (0, import_react.useState)(false);
2475
3619
  const [err, setErr] = (0, import_react.useState)(null);
2476
3620
  const [busy, setBusy] = (0, import_react.useState)(false);
2477
3621
  const [lanPeers, setLanPeers] = (0, import_react.useState)([]);
3622
+ const [presets, setPresets] = (0, import_react.useState)([]);
2478
3623
  const ok = f.name.trim() && !busy;
2479
3624
  const [ws, setWs] = (0, import_react.useState)(d && d.workspace ? d.workspace : null);
3625
+ (0, import_react.useEffect)(() => {
3626
+ a.presets().then(setPresets).catch(() => {
3627
+ });
3628
+ }, []);
3629
+ const togglePreset = (p) => {
3630
+ const has = f.agents.some((x) => x.key === p.key);
3631
+ setF({
3632
+ ...f,
3633
+ agents: has ? f.agents.filter((x) => x.key !== p.key) : [...f.agents, { key: p.key, name: p.name, persona: p.persona, systemPrompt: p.systemPrompt, scope: p.scope, moderator: !!p.moderator, reasoningEffort: p.reasoningEffort || "medium", approvalRequired: false, model: "" }]
3634
+ });
3635
+ };
3636
+ const addCustomDraft = () => {
3637
+ if (!draft.name.trim()) {
3638
+ setErr("Custom agent needs a name");
3639
+ setTab("agents");
3640
+ return;
3641
+ }
3642
+ setF({
3643
+ ...f,
3644
+ agents: [...f.agents, { ...draft, name: draft.name.trim(), key: void 0 }]
3645
+ });
3646
+ setDraft({ name: "", persona: "", systemPrompt: "", scope: "read", moderator: false, reasoningEffort: "medium", approvalRequired: false, model: "" });
3647
+ setShowCustom(false);
3648
+ setErr(null);
3649
+ };
3650
+ const removeDraft = (i) => setF({ ...f, agents: f.agents.filter((_, j) => j !== i) });
2480
3651
  const submit = async () => {
2481
3652
  if (!ok) return;
2482
3653
  if (!ws) {
2483
3654
  setErr("Pick a workspace");
3655
+ setTab("room");
2484
3656
  return;
2485
3657
  }
2486
3658
  setBusy(true);
2487
3659
  setErr(null);
3660
+ const transport = f.transport;
3661
+ const localOnly = transport === "local";
3662
+ const mode = transport === "cloudflare" ? "relay" : "lan";
3663
+ const lanHostPort = transport === "relay" ? f.lanHostPort.trim() || "62.234.113.56:80" : transport === "lan" ? "" : void 0;
2488
3664
  try {
2489
3665
  await a.create({
2490
3666
  name: f.name.trim(),
2491
3667
  description: f.description.trim(),
2492
3668
  tags: f.tags,
2493
- public: f.public,
2494
- mode: f.mode,
2495
- lanHostPort: f.mode === "lan" ? f.lanHostPort.trim() : void 0,
3669
+ public: localOnly ? false : f.public,
3670
+ mode,
3671
+ lanHostPort,
2496
3672
  workspacePath: ws.path,
2497
- workspaceName: ws.title
3673
+ workspaceName: ws.title,
3674
+ localOnly,
3675
+ turnLimit: f.turnLimit,
3676
+ concurrency: f.concurrency,
3677
+ isolation: f.isolation,
3678
+ agents: f.agents.map((ag) => ag.key ? ag.key : { name: ag.name, persona: ag.persona, systemPrompt: ag.systemPrompt, scope: ag.scope, moderator: ag.moderator, reasoningEffort: ag.reasoningEffort, approvalRequired: ag.approvalRequired, model: ag.model || void 0 })
2498
3679
  });
2499
3680
  } catch (e) {
2500
3681
  setErr(e.message);
@@ -2509,6 +3690,11 @@ function CreateDialog({ store, d }) {
2509
3690
  setErr(e.message);
2510
3691
  }
2511
3692
  };
3693
+ const tabs = [
3694
+ { id: "room", label: "Room" },
3695
+ { id: "agents", label: `Agents${f.agents.length ? ` (${f.agents.length})` : ""}` },
3696
+ { id: "limits", label: "Limits" }
3697
+ ];
2512
3698
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2513
3699
  import_dsh_client_ui_primitives.Modal,
2514
3700
  {
@@ -2516,42 +3702,128 @@ function CreateDialog({ store, d }) {
2516
3702
  onClose: a.closeDialog,
2517
3703
  title: "Create room",
2518
3704
  closeLabel: "Close",
2519
- className: "rm-modal-wide",
2520
- description: ws ? `In workspace \u201C${ws.title}\u201D (${ws.path}). Agent cwd + MCP sandbox = this path. Default relay 62.234.113.56:80 \xB7 E2EE.` : "Pick a dsh workspace. Default relay 62.234.113.56:80 \xB7 E2EE.",
3705
+ className: "rm-modal-wide rm-modal-create rm-modal-scrollable",
3706
+ contentClassName: "rm-modal-scroll",
3707
+ description: ws ? `Workspace \u201C${ws.title}\u201D \xB7 default VPS relay 62.234.113.56:80 \xB7 E2EE. Every option below is editable.` : "Pick a dsh workspace. Every create option is on these tabs \u2014 nothing is hidden off-screen.",
2521
3708
  footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2522
3709
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Cancel" }),
2523
3710
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "primary", disabled: !ok, onClick: submit, children: busy ? "Creating\u2026" : "Create" })
2524
3711
  ] }),
2525
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-form rm-form-wide", onKeyDown: (e) => e.key === "Enter" && e.target.tagName === "INPUT" && submit(), children: [
2526
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Workspace", htmlFor: "rm-create-ws", hint: "Native dsh workspace this room belongs to.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WorkspaceSelect, { store, id: "rm-create-ws", value: ws, onChange: setWs }) }),
2527
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Name", htmlFor: "rm-create-name", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-name-row", children: [
2528
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: f.name || "room", size: 36 }),
2529
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-name", autoFocus: true, placeholder: "e.g. release-crew", maxLength: 60, value: f.name, onChange: (e) => setF({ ...f, name: e.target.value }) })
2530
- ] }) }),
2531
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Transport", hint: "VPS directory works on LAN/VPS. Cloudflare is optional.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-mode-row", children: [
2532
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (f.mode === "lan" ? " is-on" : ""), onClick: () => setF({ ...f, mode: "lan", lanHostPort: f.lanHostPort || "62.234.113.56:80" }), children: "LAN / VPS" }),
2533
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (f.mode === "relay" ? " is-on" : ""), onClick: () => setF({ ...f, mode: "relay" }), children: "Cloudflare" })
2534
- ] }) }),
2535
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Description", htmlFor: "rm-create-desc", hint: "Shown to members and in public search.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-desc", placeholder: "e.g. Agents that review and ship releases", maxLength: 280, value: f.description, onChange: (e) => setF({ ...f, description: e.target.value }) }) }),
2536
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Tags", htmlFor: "rm-create-tags", hint: "Spaces or commas, up to 8.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-tags", placeholder: "e.g. review rust", value: f.tags, onChange: (e) => setF({ ...f, tags: e.target.value }) }) }),
2537
- f.mode === "lan" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Field, { label: "Relay host:port", htmlFor: "rm-lan-hp", hint: "Default 62.234.113.56:80. Blank = this machine + mDNS.", children: [
2538
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-lan-hp", placeholder: "62.234.113.56:80 or 100.x.x.x:7421", value: f.lanHostPort, onChange: (e) => setF({ ...f, lanHostPort: e.target.value }) }),
2539
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-lan-browse", children: [
3712
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-create", "data-rm-create": "", onKeyDown: (e) => e.key === "Enter" && e.target.tagName === "INPUT" && tab === "room" && submit(), children: [
3713
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-tabs", role: "tablist", "aria-label": "Create room sections", children: tabs.map((t) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", role: "tab", "aria-selected": tab === t.id, className: "rm-tab" + (tab === t.id ? " is-on" : ""), onClick: () => setTab(t.id), children: t.label }, t.id)) }),
3714
+ tab === "room" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-form rm-form-wide rm-create-pane", role: "tabpanel", children: [
3715
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Workspace", htmlFor: "rm-create-ws", hint: "Native dsh workspace this room belongs to.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WorkspaceSelect, { store, id: "rm-create-ws", value: ws, onChange: setWs }) }),
3716
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Name", htmlFor: "rm-create-name", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-name-row", children: [
3717
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: f.name || "room", size: 36 }),
3718
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-name", autoFocus: true, placeholder: "e.g. release-crew", maxLength: 60, value: f.name, onChange: (e) => setF({ ...f, name: e.target.value }) })
3719
+ ] }) }),
3720
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Transport", hint: "Local only = in-process (no network). Relay / LAN / Cloudflare for remote hosts.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-mode-row rm-mode-wrap", children: [
3721
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (f.transport === "local" ? " is-on" : ""), onClick: () => setF({ ...f, transport: "local", public: false }), children: "Local only" }),
3722
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (f.transport === "relay" ? " is-on" : ""), onClick: () => setF({ ...f, transport: "relay", lanHostPort: f.lanHostPort || "62.234.113.56:80" }), children: "Relay (VPS)" }),
3723
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (f.transport === "lan" ? " is-on" : ""), onClick: () => setF({ ...f, transport: "lan" }), children: "LAN (this machine)" }),
3724
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (f.transport === "cloudflare" ? " is-on" : ""), onClick: () => setF({ ...f, transport: "cloudflare" }), children: "Cloudflare" })
3725
+ ] }) }) }),
3726
+ f.transport === "relay" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Relay host:port", htmlFor: "rm-lan-hp", hint: "Default 62.234.113.56:80 (public directory).", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-lan-hp", placeholder: "62.234.113.56:80", value: f.lanHostPort, onChange: (e) => setF({ ...f, lanHostPort: e.target.value }) }) }) }) : null,
3727
+ f.transport === "lan" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "LAN relay", hint: "Blank host embeds a relay on this machine and advertises it over mDNS.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-lan-browse", children: [
2540
3728
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", onClick: browseLan, children: "Browse mDNS" }),
2541
- lanPeers.map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-lan-peer", onClick: () => setF({ ...f, lanHostPort: p.hostPort }), children: [
3729
+ lanPeers.map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "rm-lan-peer", onClick: () => setF({ ...f, transport: "relay", lanHostPort: p.hostPort }), children: [
2542
3730
  p.hostPort,
2543
3731
  p.name ? ` \xB7 ${p.name}` : ""
2544
3732
  ] }, p.hostPort))
3733
+ ] }) }) }) : null,
3734
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Description", htmlFor: "rm-create-desc", hint: "Shown to members and in public search.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-desc", placeholder: "e.g. Agents that review and ship releases", maxLength: 280, value: f.description, onChange: (e) => setF({ ...f, description: e.target.value }) }) }),
3735
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Tags", htmlFor: "rm-create-tags", hint: "Spaces or commas, up to 8.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-tags", placeholder: "e.g. review rust", value: f.tags, onChange: (e) => setF({ ...f, tags: e.target.value }) }) }),
3736
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-span-2 rm-switch-row", children: [
3737
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
3738
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "List in public directory" }),
3739
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: f.transport === "local" ? "Local-only rooms stay private (no network)." : f.public ? "Anyone can find and join \u2014 key is published with the listing." : "Private: invite only." })
3740
+ ] }),
3741
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: f.public && f.transport !== "local", onChange: (v) => setF({ ...f, public: v }), label: "List in public directory", disabled: f.transport === "local", title: f.transport === "local" ? "Local-only rooms stay private" : void 0 })
2545
3742
  ] })
2546
- ] }) }) : null,
2547
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-span-2 rm-switch-row", children: [
2548
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
2549
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "List in public directory" }),
2550
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: f.public ? "Anyone can find and join \u2014 key is published with the listing." : "Private: invite only." })
3743
+ ] }) : null,
3744
+ tab === "agents" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-create-pane", role: "tabpanel", children: [
3745
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Presets" }),
3746
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "Shortcuts that seed a local agent. You can still add free-form agents below, and edit anyone after create." }),
3747
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-preset-row", children: (presets.length ? presets : [
3748
+ { key: "moderator", name: "Moderator", persona: "Breaks requests into tasks and summarizes", scope: "read", moderator: true, reasoningEffort: "medium", systemPrompt: "" },
3749
+ { key: "architect", name: "Architect", persona: "Designs the approach", scope: "read", reasoningEffort: "high", systemPrompt: "" },
3750
+ { key: "coder", name: "Coder", persona: "Implements in its own worktree", scope: "write", reasoningEffort: "medium", systemPrompt: "" },
3751
+ { key: "reviewer", name: "Reviewer", persona: "Reviews diffs", scope: "read", reasoningEffort: "high", systemPrompt: "" }
3752
+ ]).map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "rm-preset rm-preset-check" + (f.agents.some((x) => x.key === p.key) ? " is-on" : ""), children: [
3753
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", checked: f.agents.some((x) => x.key === p.key), onChange: () => togglePreset(p) }),
3754
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
3755
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: p.name }),
3756
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-preset-sub", children: p.persona })
3757
+ ] })
3758
+ ] }, p.key)) }),
3759
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-create-custom", children: [
3760
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-create-custom-head", children: [
3761
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
3762
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Custom agents" }),
3763
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "Free-form name, persona, system prompt, model, scope, approval, moderator." })
3764
+ ] }),
3765
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlusIcon, {}), onClick: () => {
3766
+ setShowCustom(true);
3767
+ setErr(null);
3768
+ }, children: "Add custom" })
3769
+ ] }),
3770
+ f.agents.filter((x) => !x.key).length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "rm-draft-list", children: f.agents.map((ag, i) => ag.key ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: "rm-draft-item", children: [
3771
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: ag.name, size: 24 }),
3772
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-draft-main", children: [
3773
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: ag.name }),
3774
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
3775
+ ag.persona || "custom",
3776
+ " \xB7 ",
3777
+ ag.scope,
3778
+ ag.moderator ? " \xB7 moderator" : ""
3779
+ ] })
3780
+ ] }),
3781
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mini", "aria-label": `Remove ${ag.name}`, onClick: () => removeDraft(i), children: "\xD7" })
3782
+ ] }, i)) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "No custom agents yet \u2014 presets above, or Add custom." }),
3783
+ showCustom ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-draft-form rm-form rm-form-wide", children: [
3784
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Name", htmlFor: "rm-create-ag-name", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-ag-name", value: draft.name, maxLength: 40, onChange: (e) => setDraft({ ...draft, name: e.target.value }), placeholder: "e.g. Data wrangler" }) }),
3785
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Persona", htmlFor: "rm-create-ag-persona", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-ag-persona", value: draft.persona, maxLength: 200, onChange: (e) => setDraft({ ...draft, persona: e.target.value }), placeholder: "One-line role" }) }),
3786
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "System prompt", htmlFor: "rm-create-ag-sp", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("textarea", { id: "rm-create-ag-sp", className: "rm-input rm-textarea", rows: 3, value: draft.systemPrompt, maxLength: 4e3, onChange: (e) => setDraft({ ...draft, systemPrompt: e.target.value }) }) }) }),
3787
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Model", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { className: "rm-input", "aria-label": "Model", value: draft.model, onChange: (e) => setDraft({ ...draft, model: e.target.value }), children: MODEL_CHOICES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: m, children: m || "dsh default model" }, m)) }) }),
3788
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Reasoning effort", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { className: "rm-input", "aria-label": "Reasoning effort", value: draft.reasoningEffort, onChange: (e) => setDraft({ ...draft, reasoningEffort: e.target.value }), children: ["low", "medium", "high", "max"].map((x) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: x, children: x }, x)) }) }),
3789
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Workspace scope", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-mode-row", children: [
3790
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (draft.scope === "read" ? " is-on" : ""), onClick: () => setDraft({ ...draft, scope: "read" }), children: "Read-only" }),
3791
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mode-btn" + (draft.scope === "write" ? " is-on" : ""), onClick: () => setDraft({ ...draft, scope: "write" }), children: "Read + write" })
3792
+ ] }) }),
3793
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-col", children: [
3794
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-row", children: [
3795
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Approval required" }) }),
3796
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: draft.approvalRequired, onChange: (v) => setDraft({ ...draft, approvalRequired: v }), label: "Approval required" })
3797
+ ] }),
3798
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-row", children: [
3799
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Moderator" }) }),
3800
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: draft.moderator, onChange: (v) => setDraft({ ...draft, moderator: v }), label: "Moderator" })
3801
+ ] })
3802
+ ] }),
3803
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-span-2 rm-actions", children: [
3804
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", onClick: () => setShowCustom(false), children: "Cancel" }),
3805
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "primary", onClick: addCustomDraft, children: "Add to room" })
3806
+ ] })
3807
+ ] }) : null
2551
3808
  ] }),
2552
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: f.public, onChange: (v) => setF({ ...f, public: v }), label: "List in public directory" })
2553
- ] }),
2554
- err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2 rm-error", children: err }) : null
3809
+ f.agents.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-note", style: { marginTop: 10 }, children: [
3810
+ "Will create: ",
3811
+ f.agents.map((x) => x.name).join(", "),
3812
+ ". Edit or add more after create from Local agents \u2192 Add local agent."
3813
+ ] }) : null
3814
+ ] }) : null,
3815
+ tab === "limits" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-form rm-form-wide rm-create-pane", role: "tabpanel", children: [
3816
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Parallel runs", htmlFor: "rm-create-conc", hint: "Max local agent runs at once (1\u201316). The rest wait in a visible queue.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-conc", type: "number", min: 1, max: 16, value: String(f.concurrency), onChange: (e) => setF({ ...f, concurrency: Math.min(16, Math.max(1, Number(e.target.value) || 1)) }) }) }),
3817
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Turn limit", htmlFor: "rm-create-turns", hint: "Max agent-to-agent hand-offs per task/thread (1\u2013100). A human message resets it.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Input, { id: "rm-create-turns", type: "number", min: 1, max: 100, value: String(f.turnLimit), onChange: (e) => setF({ ...f, turnLimit: Math.min(100, Math.max(1, Number(e.target.value) || 6)) }) }) }),
3818
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Workspace isolation", hint: "Writing agents get their own git worktree by default when the workspace is a git repo.", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { className: "rm-input", "aria-label": "Isolation mode", value: f.isolation, onChange: (e) => setF({ ...f, isolation: e.target.value }), children: [
3819
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "auto", children: "Auto \u2014 git worktree per writing agent, else file locks" }),
3820
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "worktree", children: "Git worktree per agent" }),
3821
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "locks", children: "File locks (shared workspace)" }),
3822
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "off", children: "Off (shared, no locks)" })
3823
+ ] }) }) }),
3824
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2 rm-note", children: "These apply as soon as the room has local agents. Change them later under Runs / Workspace isolation in the room panel." })
3825
+ ] }) : null,
3826
+ err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-error", style: { marginTop: 10 }, children: err }) : null
2555
3827
  ] })
2556
3828
  }
2557
3829
  );
@@ -2891,6 +4163,8 @@ function Overlay({ store }) {
2891
4163
  d?.kind === "config" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConfigDialog, { store, d }) : null,
2892
4164
  d?.kind === "workspace" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WorkspaceDialog, { store }) : null,
2893
4165
  d?.kind === "browse" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BrowseDialog, { store }) : null,
4166
+ d?.kind === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AgentDialog, { store, d }) : null,
4167
+ d?.kind === "merge" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MergeDialog, { store, d }) : null,
2894
4168
  s.toast ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Toast, { text: s.toast, onDone: store.actions.clearToast, holdMs: 2200 }) : null
2895
4169
  ] });
2896
4170
  }