@cecwxf/dsh-room 0.2.0 → 0.3.0

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';
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,371 @@ 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: "Host several agents from this dsh as separate members \u2014 each with 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 local 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 [f, setF] = (0, import_react.useState)(() => existing ? { 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 } : { preset: "", name: "", persona: "", systemPrompt: "", model: "", reasoningEffort: "medium", scope: "read", approvalRequired: false, moderator: false });
1099
+ const [err, setErr] = (0, import_react.useState)(null);
1100
+ const [busy, setBusy] = (0, import_react.useState)(false);
1101
+ (0, import_react.useEffect)(() => {
1102
+ a.presets().then(setPresets).catch(() => {
1103
+ });
1104
+ }, []);
1105
+ 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" });
1106
+ const submit = async () => {
1107
+ if (!f.name.trim()) {
1108
+ setErr("Name required");
1109
+ return;
1110
+ }
1111
+ setBusy(true);
1112
+ setErr(null);
1113
+ try {
1114
+ const body = { name: f.name, persona: f.persona, systemPrompt: f.systemPrompt, model: f.model, reasoningEffort: f.reasoningEffort, scope: f.scope, approvalRequired: f.approvalRequired, moderator: f.moderator, preset: f.preset || void 0 };
1115
+ if (existing) await a.updateAgent(d.roomId, existing.agentId, body);
1116
+ else {
1117
+ const r = await a.addAgent(d.roomId, body);
1118
+ a.toast(`${r.name} joined as @${r.agentId}`);
1119
+ }
1120
+ a.closeDialog();
1121
+ } catch (e) {
1122
+ setErr(e.message);
1123
+ setBusy(false);
1124
+ }
1125
+ };
1126
+ const remove = async () => {
1127
+ setBusy(true);
1128
+ try {
1129
+ const r = await a.removeAgent(d.roomId, existing.agentId);
1130
+ a.toast(`Removed ${existing.name}${r.branchKept ? ` \xB7 branch ${r.branchKept} kept` : ""}`);
1131
+ a.closeDialog();
1132
+ } catch (e) {
1133
+ setErr(e.message);
1134
+ setBusy(false);
1135
+ }
1136
+ };
1137
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1138
+ import_dsh_client_ui_primitives.Modal,
1139
+ {
1140
+ open: true,
1141
+ onClose: a.closeDialog,
1142
+ title: existing ? `Edit ${existing.name}` : "Add local agent",
1143
+ closeLabel: "Close",
1144
+ className: "rm-modal-wide",
1145
+ description: existing ? `Member @${existing.agentId} \xB7 own identity and dsh session in this room's workspace.` : "Runs on this dsh as a separate room member: own keypair, own session, own scope. Remote peers see it as a member.",
1146
+ footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1147
+ 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,
1148
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { flex: 1 } }),
1149
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Cancel" }),
1150
+ /* @__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" })
1151
+ ] }),
1152
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-form rm-form-wide", "data-rm-agent-form": "", children: [
1153
+ !existing ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-span-2", children: [
1154
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Preset" }),
1155
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-preset-row", children: 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: [
1156
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: p.name }),
1157
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: p.persona })
1158
+ ] }, p.key)) })
1159
+ ] }) : null,
1160
+ /* @__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) => setF({ ...f, name: e.target.value }), placeholder: "e.g. Coder" }) }),
1161
+ /* @__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 }), placeholder: "e.g. Implements changes" }) }),
1162
+ /* @__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 }) }) }) }),
1163
+ /* @__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: [
1164
+ MODEL_CHOICES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: m, children: m || "dsh default model" }, m)),
1165
+ f.model && !MODEL_CHOICES.includes(f.model) ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: f.model, children: f.model }) : null
1166
+ ] }) }),
1167
+ /* @__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)) }) }),
1168
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Workspace scope", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-mode-row", children: [
1169
+ /* @__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" }),
1170
+ /* @__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" })
1171
+ ] }) }),
1172
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-col", children: [
1173
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-row", children: [
1174
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1175
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Approval required" }),
1176
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "Each run waits for your OK." })
1177
+ ] }),
1178
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: f.approvalRequired, onChange: (v) => setF({ ...f, approvalRequired: v }), label: "Approval required" })
1179
+ ] }),
1180
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-row", children: [
1181
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1182
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Moderator" }),
1183
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "Splits requests into tasks and summarizes. One per room." })
1184
+ ] }),
1185
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: f.moderator, onChange: (v) => setF({ ...f, moderator: v }), label: "Moderator" })
1186
+ ] })
1187
+ ] }),
1188
+ 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,
1189
+ err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2 rm-error", children: err }) : null
1190
+ ] })
1191
+ }
1192
+ );
1193
+ }
1194
+ function DiffView({ patch }) {
1195
+ if (!patch) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "No changes." });
1196
+ 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)) });
1197
+ }
1198
+ function MergeDialog({ store, d }) {
1199
+ const a = store.actions;
1200
+ const [data, setData] = (0, import_react.useState)(null);
1201
+ const [sel, setSel] = (0, import_react.useState)(null);
1202
+ const [confirm, setConfirm] = (0, import_react.useState)(null);
1203
+ const [busy, setBusy] = (0, import_react.useState)(false);
1204
+ const [result, setResult] = (0, import_react.useState)(null);
1205
+ const load = async () => {
1206
+ try {
1207
+ const r = await a.diffs(d.roomId);
1208
+ setData(r);
1209
+ setSel((cur) => cur && r.agents.some((x) => x.agentId === cur) ? cur : (r.agents.find((x) => x.ahead) || r.agents[0] || {}).agentId || null);
1210
+ } catch (e) {
1211
+ setData({ error: e.message, agents: [] });
1212
+ }
1213
+ };
1214
+ (0, import_react.useEffect)(() => {
1215
+ void load();
1216
+ }, [d.roomId]);
1217
+ const ag = data && data.agents.find((x) => x.agentId === sel);
1218
+ const run = async (kind) => {
1219
+ setBusy(true);
1220
+ setResult(null);
1221
+ try {
1222
+ const r = kind === "merge" ? await a.merge(d.roomId, sel) : await a.discard(d.roomId, sel);
1223
+ 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." });
1224
+ await load();
1225
+ } catch (e) {
1226
+ setResult({ ok: false, text: e.message });
1227
+ } finally {
1228
+ setBusy(false);
1229
+ setConfirm(null);
1230
+ }
1231
+ };
1232
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1233
+ import_dsh_client_ui_primitives.Modal,
1234
+ {
1235
+ open: true,
1236
+ onClose: a.closeDialog,
1237
+ title: "Review & merge agent changes",
1238
+ closeLabel: "Close",
1239
+ className: "rm-modal-wide rm-modal-xl",
1240
+ 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.",
1241
+ footer: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Close" }),
1242
+ 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: [
1243
+ /* @__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: () => {
1244
+ setSel(x.agentId);
1245
+ setConfirm(null);
1246
+ setResult(null);
1247
+ }, children: [
1248
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: x.name, size: 24 }),
1249
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-merge-agent-body", children: [
1250
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: x.name }),
1251
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-mono", children: x.branch || (x.isolation === "locks" ? "shared workspace \xB7 locks" : "no worktree yet") })
1252
+ ] }),
1253
+ 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: [
1254
+ x.files.length,
1255
+ " file",
1256
+ x.files.length === 1 ? "" : "s"
1257
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "quiet", children: "clean" })
1258
+ ] }, x.agentId)) }),
1259
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-merge-main", children: ag ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1260
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-merge-head", children: [
1261
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1262
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: ag.name }),
1263
+ " ",
1264
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-mono", children: ag.branch || "" }),
1265
+ ag.ahead ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-note", style: { margin: "0 0 0 8px" }, children: [
1266
+ ag.ahead,
1267
+ " commit(s) ahead"
1268
+ ] }) : null
1269
+ ] }),
1270
+ /* @__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: [
1271
+ /* @__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?` }),
1272
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", onClick: () => setConfirm(null), children: "Cancel" }),
1273
+ /* @__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" })
1274
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1275
+ /* @__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" }),
1276
+ /* @__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" })
1277
+ ] }) })
1278
+ ] }),
1279
+ result ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: result.ok ? "rm-ok" : "rm-error", children: result.text }) : null,
1280
+ ag.conflicts && ag.conflicts.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-error", children: [
1281
+ "Would conflict with the main workspace: ",
1282
+ ag.conflicts.join(", "),
1283
+ " \u2014 resolve on the branch or discard."
1284
+ ] }) : null,
1285
+ ag.mainDirty && ag.mainDirty.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-error", children: [
1286
+ "Uncommitted changes in the main workspace touch: ",
1287
+ ag.mainDirty.join(", ")
1288
+ ] }) : null,
1289
+ 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: [
1290
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-mono", children: f.path }),
1291
+ " ",
1292
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-diff-addn", children: [
1293
+ "+",
1294
+ f.adds
1295
+ ] }),
1296
+ " ",
1297
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-diff-deln", children: [
1298
+ "\u2212",
1299
+ f.dels
1300
+ ] })
1301
+ ] }, f.path)) }) : null,
1302
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DiffView, { patch: ag.patch }),
1303
+ ag.truncated ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "Diff truncated." }) : null
1304
+ ] }) : null })
1305
+ ] }) })
1306
+ }
1307
+ );
1308
+ }
852
1309
  function EmptyMain({ store }) {
853
1310
  const a = store.actions;
854
1311
  const workspaces = useNativeWorkspaces(store);
@@ -908,11 +1365,25 @@ function RoomsMain({ store }) {
908
1365
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-chat-header-title", children: room.name }),
909
1366
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-chat-header-sub", children: [
910
1367
  room.members.filter((m) => m.online !== false).length,
911
- " online \xB7 ",
1368
+ " online",
1369
+ room.local && room.local.agents.length ? ` \xB7 ${room.local.agents.length} local agent${room.local.agents.length === 1 ? "" : "s"}` : "",
1370
+ " \xB7 ",
912
1371
  room.public ? "public" : "private",
913
1372
  room.status !== "online" ? ` \xB7 room ${room.status}` : ""
914
1373
  ] })
915
1374
  ] }),
1375
+ (() => {
1376
+ const act = (room.runs || []).filter((r) => r.state === "running" || r.state === "queued" || r.state === "approval");
1377
+ if (!act.length) return null;
1378
+ const running = act.filter((r) => r.state === "running").length;
1379
+ 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: [
1380
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StopIcon, {}),
1381
+ "Stop \xB7 ",
1382
+ running,
1383
+ " running",
1384
+ act.length > running ? `, ${act.length - running} queued` : ""
1385
+ ] });
1386
+ })(),
916
1387
  room.public ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-badge rm-badge-public", children: [
917
1388
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GlobeIcon, {}),
918
1389
  "Public"
@@ -920,10 +1391,10 @@ function RoomsMain({ store }) {
920
1391
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LockIcon, {}),
921
1392
  "Private"
922
1393
  ] }),
923
- room.transport === "lan" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-badge", children: "LAN/VPS" }) : null,
1394
+ 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
1395
  /* @__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
1396
  ] }),
926
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChatBody, { messages }),
1397
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChatBody, { messages, members: room.members }),
927
1398
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Composer, { room, store, draft, setDraft })
928
1399
  ] }),
929
1400
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -953,7 +1424,7 @@ function Field({ label, hint, children, htmlFor }) {
953
1424
  function CreateDialog({ store, d }) {
954
1425
  const s = useStore(store);
955
1426
  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" });
1427
+ const [f, setF] = (0, import_react.useState)({ name: "", description: "", tags: "", public: false, mode: "lan", lanHostPort: "62.234.113.56:80", localOnly: false, agents: [] });
957
1428
  const [err, setErr] = (0, import_react.useState)(null);
958
1429
  const [busy, setBusy] = (0, import_react.useState)(false);
959
1430
  const [lanPeers, setLanPeers] = (0, import_react.useState)([]);
@@ -976,7 +1447,9 @@ function CreateDialog({ store, d }) {
976
1447
  mode: f.mode,
977
1448
  lanHostPort: f.mode === "lan" ? f.lanHostPort.trim() : void 0,
978
1449
  workspacePath: ws.path,
979
- workspaceName: ws.title
1450
+ workspaceName: ws.title,
1451
+ localOnly: f.localOnly,
1452
+ agents: f.agents
980
1453
  });
981
1454
  } catch (e) {
982
1455
  setErr(e.message);
@@ -1026,12 +1499,29 @@ function CreateDialog({ store, d }) {
1026
1499
  ] }, p.hostPort))
1027
1500
  ] })
1028
1501
  ] }) }) : null,
1502
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-span-2 rm-local-box", children: [
1503
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-switch-row", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1504
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Local agents" }),
1505
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "Host several agents from this dsh as separate members (own identity, session, scope). Edit them later in room details." })
1506
+ ] }) }),
1507
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-preset-row", children: [["moderator", "Moderator"], ["architect", "Architect"], ["coder", "Coder"], ["reviewer", "Reviewer"]].map(([k, label]) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "rm-preset rm-preset-check" + (f.agents.includes(k) ? " is-on" : ""), children: [
1508
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", checked: f.agents.includes(k), onChange: (e) => setF({ ...f, agents: e.target.checked ? [...f.agents, k] : f.agents.filter((x) => x !== k) }) }),
1509
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: label })
1510
+ ] }, k)) }),
1511
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-row", children: [
1512
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1513
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Local only (no network)" }),
1514
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "In-process loopback \u2014 nothing leaves this machine. One click to switch to Relay/LAN later." })
1515
+ ] }),
1516
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: f.localOnly, onChange: (v) => setF({ ...f, localOnly: v, public: v ? false : f.public }), label: "Local only" })
1517
+ ] })
1518
+ ] }),
1029
1519
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-span-2 rm-switch-row", children: [
1030
1520
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1031
1521
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "List in public directory" }),
1032
1522
  /* @__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." })
1033
1523
  ] }),
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" })
1524
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: f.public, onChange: (v) => setF({ ...f, public: v, localOnly: v ? false : f.localOnly }), label: "List in public directory" })
1035
1525
  ] }),
1036
1526
  err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2 rm-error", children: err }) : null
1037
1527
  ] })
@@ -1373,6 +1863,8 @@ function Overlay({ store }) {
1373
1863
  d?.kind === "config" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConfigDialog, { store, d }) : null,
1374
1864
  d?.kind === "workspace" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WorkspaceDialog, { store }) : null,
1375
1865
  d?.kind === "browse" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BrowseDialog, { store }) : null,
1866
+ d?.kind === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AgentDialog, { store, d }) : null,
1867
+ d?.kind === "merge" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MergeDialog, { store, d }) : null,
1376
1868
  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
1869
  ] });
1378
1870
  }
@@ -1555,7 +2047,7 @@ var import_react = require("react");
1555
2047
  var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
1556
2048
 
1557
2049
  // 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';
2050
+ 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';
1559
2051
 
1560
2052
  // src/client/store.js
1561
2053
  var API = "/api/collab";
@@ -1733,6 +2225,37 @@ function createRoomsStore() {
1733
2225
  },
1734
2226
  async mcp(roomId, tool, args, target) {
1735
2227
  return post(`/rooms/${encodeURIComponent(roomId)}/mcp`, { tool, args, target });
2228
+ },
2229
+ // ── local multi-agent ──
2230
+ async presets() {
2231
+ return (await call("/presets")).presets;
2232
+ },
2233
+ async addAgent(roomId, body) {
2234
+ return post(`/rooms/${encodeURIComponent(roomId)}/agents`, body);
2235
+ },
2236
+ async updateAgent(roomId, id, body) {
2237
+ return post(`/rooms/${encodeURIComponent(roomId)}/agents/${encodeURIComponent(id)}`, body);
2238
+ },
2239
+ async removeAgent(roomId, id) {
2240
+ return post(`/rooms/${encodeURIComponent(roomId)}/agents/${encodeURIComponent(id)}?op=delete`);
2241
+ },
2242
+ async saveLocal(roomId, patch) {
2243
+ return post(`/rooms/${encodeURIComponent(roomId)}/local`, patch);
2244
+ },
2245
+ async setTransport(roomId, target, force) {
2246
+ return post(`/rooms/${encodeURIComponent(roomId)}/transport`, { target, force: !!force });
2247
+ },
2248
+ async stop(roomId, agentId) {
2249
+ return post(`/rooms/${encodeURIComponent(roomId)}/stop`, agentId ? { agentId } : {});
2250
+ },
2251
+ async diffs(roomId) {
2252
+ return call(`/rooms/${encodeURIComponent(roomId)}/diffs`);
2253
+ },
2254
+ async merge(roomId, agentId) {
2255
+ return post(`/rooms/${encodeURIComponent(roomId)}/merge`, { agentId, approve: true });
2256
+ },
2257
+ async discard(roomId, agentId) {
2258
+ return post(`/rooms/${encodeURIComponent(roomId)}/discard`, { agentId, approve: true });
1736
2259
  }
1737
2260
  };
1738
2261
  const api = {
@@ -1788,6 +2311,17 @@ var LockIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { size: 1
1788
2311
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "3.5", y: "7", width: "9", height: "6.25", rx: "1.5" }),
1789
2312
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5.5 7V5.25a2.5 2.5 0 0 1 5 0V7" })
1790
2313
  ] });
2314
+ var BotIcon = ({ size = 14 }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { size, children: [
2315
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "3", y: "5", width: "10", height: "7.5", rx: "2" }),
2316
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 2.5V5M6 8.5h.01M10 8.5h.01M6.25 10.75h3.5" })
2317
+ ] });
2318
+ 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" }) });
2319
+ var MergeIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { size: 14, children: [
2320
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "5", cy: "3.75", r: "1.5" }),
2321
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "5", cy: "12.25", r: "1.5" }),
2322
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "11", cy: "8", r: "1.5" }),
2323
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 5.25v5.5M5 5.5c0 2 1.5 2.5 4.5 2.5" })
2324
+ ] });
1791
2325
  var PeopleIcon = () => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Svg, { size: 14, children: [
1792
2326
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "6", cy: "5.5", r: "2.25" }),
1793
2327
  /* @__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 +2442,10 @@ function RoomsNav({ store }) {
1908
2442
  }
1909
2443
  function RoomSessionMark({ store, sessionId }) {
1910
2444
  const s = useStore(store);
2445
+ for (const r of s.rooms || []) {
2446
+ const ag = (r.local && r.local.agents || []).find((x) => x.sessionId === sessionId);
2447
+ 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 }) });
2448
+ }
1911
2449
  const room = (s.rooms || []).find((r) => r.sessionId === sessionId);
1912
2450
  if (!room) return null;
1913
2451
  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 +2471,14 @@ function renderMentions(text) {
1933
2471
  function PanelIcon({ size }) {
1934
2472
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RoomsIcon, { size });
1935
2473
  }
1936
- function Message({ m, showAuthor }) {
2474
+ function Message({ m, showAuthor, who }) {
1937
2475
  if (m.kind === "task.create") {
1938
2476
  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
2477
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-task-head", children: [
1940
2478
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TaskIcon, {}),
1941
2479
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
1942
2480
  "Task \xB7 ",
1943
- m.from
2481
+ who && who.name || m.from
1944
2482
  ] }),
1945
2483
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "info", children: "open" }),
1946
2484
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-msg-time", children: fmtTime(m.ts) })
@@ -1953,7 +2491,7 @@ function Message({ m, showAuthor }) {
1953
2491
  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
2492
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TaskIcon, {}),
1955
2493
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
1956
- m.self ? "You" : m.from,
2494
+ m.self ? "You" : who && who.name || m.from,
1957
2495
  " ",
1958
2496
  m.kind === "task.claim" ? "claimed" : "updated",
1959
2497
  " task"
@@ -1973,14 +2511,20 @@ function Message({ m, showAuthor }) {
1973
2511
  showAuthor ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: m.from, size: 28 }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {}),
1974
2512
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1975
2513
  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 }),
2514
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-msg-author", children: who && who.name ? who.name : m.from }),
2515
+ who && who.name ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-msg-id", children: m.from }) : null,
2516
+ who && who.local ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-tag", children: [
2517
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BotIcon, { size: 11 }),
2518
+ who.moderator ? "moderator" : "local agent"
2519
+ ] }) : null,
1977
2520
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-msg-time", children: fmtTime(m.ts) })
1978
2521
  ] }) : null,
1979
2522
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-msg-body", children: renderMentions(m.text) })
1980
2523
  ] })
1981
2524
  ] });
1982
2525
  }
1983
- function ChatBody({ messages }) {
2526
+ function ChatBody({ messages, members }) {
2527
+ const byId = (0, import_react.useMemo)(() => Object.fromEntries((members || []).map((x) => [x.agentId, x])), [members]);
1984
2528
  const ref = (0, import_react.useRef)(null);
1985
2529
  (0, import_react.useEffect)(() => {
1986
2530
  const el = ref.current;
@@ -1996,7 +2540,7 @@ function ChatBody({ messages }) {
1996
2540
  prev = null;
1997
2541
  }
1998
2542
  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));
2543
+ nodes.push(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Message, { m, showAuthor, who: byId[m.from] }, m.id));
2000
2544
  prev = m;
2001
2545
  }
2002
2546
  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 +2567,7 @@ function MentionMenu({ items, active, onPick, onHover }) {
2023
2567
  onPick(it);
2024
2568
  },
2025
2569
  children: [
2026
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: it.id === "all" ? "all" : it.id, size: 22 }),
2570
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: it.id === "all" ? "all" : it.name || it.id, size: 22 }),
2027
2571
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-mention-item-body", children: [
2028
2572
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-mention-item-name", children: [
2029
2573
  "@",
@@ -2031,6 +2575,10 @@ function MentionMenu({ items, active, onPick, onHover }) {
2031
2575
  ] }),
2032
2576
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-mention-item-meta", children: it.hint })
2033
2577
  ] }),
2578
+ it.local ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-tag", children: [
2579
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BotIcon, { size: 11 }),
2580
+ "local"
2581
+ ] }) : null,
2034
2582
  it.online === false ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "quiet", children: "offline" }) : null
2035
2583
  ]
2036
2584
  },
@@ -2055,14 +2603,20 @@ function Composer({ room, store, draft, setDraft }) {
2055
2603
  if (!mq) return [];
2056
2604
  const q = mq.query.toLowerCase();
2057
2605
  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 };
2606
+ const localAgents = room.local && room.local.agents || [];
2607
+ const seen = /* @__PURE__ */ new Set();
2608
+ const known = [];
2609
+ for (const la of localAgents) {
2610
+ seen.add(la.agentId);
2611
+ 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 });
2612
+ }
2613
+ for (const m of members) {
2614
+ if (seen.has(m.agentId)) continue;
2615
+ 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 });
2616
+ }
2617
+ const all = { id: "all", hint: localAgents.length ? "Everyone \xB7 wakes all local agents in parallel" : "Everyone \xB7 wake all members", online: true };
2064
2618
  const list = [all, ...known];
2065
- return list.filter((it) => !q || it.id.toLowerCase().startsWith(q) || ("@" + it.id).toLowerCase().startsWith("@" + q));
2619
+ return list.filter((it) => !q || it.id.toLowerCase().startsWith(q) || it.name && it.name.toLowerCase().startsWith(q));
2066
2620
  })();
2067
2621
  const mc = room.memberConfig || {};
2068
2622
  const modelLabel = mc.model || "Default model";
@@ -2100,6 +2654,23 @@ function Composer({ room, store, draft, setDraft }) {
2100
2654
  a.toast(e.message);
2101
2655
  }
2102
2656
  };
2657
+ const syncCaret = (el) => {
2658
+ if (el) setCaret(el.selectionStart == null ? el.value.length : el.selectionStart);
2659
+ };
2660
+ const insertMention = (it) => {
2661
+ if (!mq || !it) return;
2662
+ const next = draft.slice(0, mq.start) + "@" + it.id + " " + draft.slice(mq.end);
2663
+ const pos = mq.start + it.id.length + 2;
2664
+ setDraft(next);
2665
+ setCaret(pos);
2666
+ requestAnimationFrame(() => {
2667
+ const el = taRef.current;
2668
+ if (el) {
2669
+ el.focus();
2670
+ el.setSelectionRange(pos, pos);
2671
+ }
2672
+ });
2673
+ };
2103
2674
  const submit = async () => {
2104
2675
  const text = draft.trim();
2105
2676
  if (!text || busy) return;
@@ -2296,7 +2867,15 @@ function RoomDetail({ room, messages, store, mention, assign }) {
2296
2867
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: m.agentId, size: 26 }),
2297
2868
  /* @__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
2869
  ] }),
2299
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-member-name", children: m.agentId }),
2870
+ /* @__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: [
2871
+ m.name,
2872
+ " ",
2873
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-msg-id", children: m.agentId })
2874
+ ] }) : m.agentId }),
2875
+ m.local ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-tag", children: [
2876
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BotIcon, { size: 11 }),
2877
+ m.moderator ? "moderator" : "local"
2878
+ ] }) : m.hostedBy && !m.self ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-agent-tag", children: "remote agent" }) : null,
2300
2879
  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
2880
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mini", title: `@mention ${m.agentId}`, onClick: () => mention(m.agentId), children: "@" }),
2302
2881
  /* @__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 +2883,9 @@ function RoomDetail({ room, messages, store, mention, assign }) {
2304
2883
  ] }, m.agentId)),
2305
2884
  !room.members.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: room.status === "online" ? "Only you are here." : "Offline." }) : null
2306
2885
  ] }),
2886
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LocalAgentsSection, { room, store, mention }),
2887
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OrchestrationSection, { room, store }),
2888
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IsolationSection, { room, store }),
2307
2889
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", children: [
2308
2890
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-h", children: [
2309
2891
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TaskIcon, {}),
@@ -2326,8 +2908,10 @@ function RoomDetail({ room, messages, store, mention, assign }) {
2326
2908
  "Invite"
2327
2909
  ] }),
2328
2910
  /* @__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." }),
2911
+ 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
2912
  /* @__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
2913
  ] }),
2914
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TransportSection, { room, store }),
2331
2915
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", children: [
2332
2916
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-detail-h", children: "About" }),
2333
2917
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("dl", { className: "rm-about", children: [
@@ -2340,7 +2924,7 @@ function RoomDetail({ room, messages, store, mention, assign }) {
2340
2924
  " Private"
2341
2925
  ] }) }),
2342
2926
  /* @__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)" }),
2927
+ /* @__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
2928
  room.memberConfig?.workspaceLabel ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2345
2929
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", { children: "Workspace" }),
2346
2930
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: room.memberConfig.workspaceLabel })
@@ -2367,6 +2951,371 @@ function RoomDetail({ room, messages, store, mention, assign }) {
2367
2951
  ] })
2368
2952
  ] });
2369
2953
  }
2954
+ var RUN_TONE = { running: "warning", queued: "info", approval: "info", done: "success", failed: "danger", stopped: "quiet" };
2955
+ var MODEL_CHOICES = ["", "deepseek-chat", "deepseek-reasoner"];
2956
+ function LocalAgentsSection({ room, store, mention }) {
2957
+ const a = store.actions;
2958
+ const local = room.local || { agents: [] };
2959
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", "data-rm-local-agents": "", children: [
2960
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-h", children: [
2961
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BotIcon, {}),
2962
+ "Local agents \xB7 ",
2963
+ local.agents.length
2964
+ ] }),
2965
+ local.agents.map((ag) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-agent-row", "data-agent-id": ag.agentId, children: [
2966
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-avatar-wrap", children: [
2967
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: ag.name, size: 26 }),
2968
+ /* @__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)" } })
2969
+ ] }),
2970
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-main", children: [
2971
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-name", children: [
2972
+ ag.name,
2973
+ ag.moderator ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-agent-tag", children: "moderator" }) : null
2974
+ ] }),
2975
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-sub rm-mono", children: [
2976
+ "@",
2977
+ ag.agentId
2978
+ ] }),
2979
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-agent-sub", children: [
2980
+ ag.scope === "write" ? ag.isolation === "worktree" ? "writes \xB7 own worktree" : ag.isolation === "locks" ? "writes \xB7 file locks" : "writes" : "read-only",
2981
+ ag.approvalRequired ? " \xB7 approval" : "",
2982
+ ag.model ? ` \xB7 ${ag.model}` : "",
2983
+ ag.reasoningEffort ? ` \xB7 ${ag.reasoningEffort}` : ""
2984
+ ] })
2985
+ ] }),
2986
+ 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,
2987
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-member-actions", children: [
2988
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "rm-mini", title: `@mention ${ag.name}`, onClick: () => mention(ag.agentId), children: "@" }),
2989
+ /* @__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" })
2990
+ ] })
2991
+ ] }, ag.agentId)),
2992
+ !local.agents.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "Host several agents from this dsh as separate members \u2014 each with its own identity, session and scope." }) : null,
2993
+ /* @__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 local agent" }) })
2994
+ ] });
2995
+ }
2996
+ function NumberSetting({ label, value, min, max, onSave, hint }) {
2997
+ const [v, setV] = (0, import_react.useState)(String(value));
2998
+ (0, import_react.useEffect)(() => setV(String(value)), [value]);
2999
+ const commit = () => {
3000
+ const n = Number(v);
3001
+ if (Number.isFinite(n) && n !== value) onSave(Math.min(max, Math.max(min, Math.round(n))));
3002
+ else setV(String(value));
3003
+ };
3004
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "rm-num-setting", title: hint, children: [
3005
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: label }),
3006
+ /* @__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() })
3007
+ ] });
3008
+ }
3009
+ function RunQueue({ room, store, compact }) {
3010
+ const a = store.actions;
3011
+ const runs = room.runs || [];
3012
+ const nameOf = (id) => (room.local && room.local.agents || []).find((x) => x.agentId === id)?.name || id;
3013
+ const active = runs.filter((r) => r.state === "running" || r.state === "queued" || r.state === "approval");
3014
+ const recent = runs.filter((r) => !active.includes(r)).slice(-3).reverse();
3015
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-runs", "data-rm-runs": "", children: [
3016
+ active.map((r, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-run rm-run-" + r.state, children: [
3017
+ /* @__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}` }),
3018
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-run-who", children: nameOf(r.agentId) }),
3019
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-run-what", title: r.summary, children: r.summary }),
3020
+ /* @__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 })
3021
+ ] }, r.id)),
3022
+ !compact ? recent.map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-run rm-run-" + r.state, children: [
3023
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-run-pos", children: "\u2713" }),
3024
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-run-who", children: nameOf(r.agentId) }),
3025
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-run-what", title: r.error || r.summary, children: r.error || r.summary }),
3026
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: RUN_TONE[r.state], children: r.state })
3027
+ ] }, r.id)) : null,
3028
+ !active.length && !recent.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "No agent runs yet." }) : null,
3029
+ 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
3030
+ ] });
3031
+ }
3032
+ function OrchestrationSection({ room, store }) {
3033
+ const a = store.actions;
3034
+ const local = room.local;
3035
+ if (!local || !local.agents.length) return null;
3036
+ const save = (patch) => a.saveLocal(room.roomId, patch).catch((e) => a.toast(e.message));
3037
+ const active = (room.runs || []).filter((r) => r.state === "running" || r.state === "queued" || r.state === "approval");
3038
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", "data-rm-orchestration": "", children: [
3039
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-h", children: [
3040
+ "Runs \xB7 ",
3041
+ active.filter((r) => r.state === "running").length,
3042
+ "/",
3043
+ local.concurrency,
3044
+ " running",
3045
+ active.length > local.concurrency ? ` \xB7 ${active.length - Math.min(local.concurrency, active.filter((r) => r.state === "running").length)} queued` : ""
3046
+ ] }),
3047
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-num-row", children: [
3048
+ /* @__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." }),
3049
+ /* @__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." })
3050
+ ] }),
3051
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RunQueue, { room, store })
3052
+ ] });
3053
+ }
3054
+ function IsolationSection({ room, store }) {
3055
+ const a = store.actions;
3056
+ const local = room.local;
3057
+ if (!local || !local.agents.some((x) => x.scope === "write")) return null;
3058
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", "data-rm-isolation": "", children: [
3059
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-h", children: [
3060
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MergeIcon, {}),
3061
+ "Workspace isolation"
3062
+ ] }),
3063
+ /* @__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: [
3064
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "auto", children: "Auto \u2014 git worktree per writing agent, else file locks" }),
3065
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "worktree", children: "Git worktree per agent" }),
3066
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "locks", children: "File locks (shared workspace)" }),
3067
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "off", children: "Off (shared, no locks)" })
3068
+ ] }),
3069
+ local.locks && local.locks.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-note", children: [
3070
+ local.locks.length,
3071
+ " file lock(s): ",
3072
+ local.locks.slice(0, 4).map((l) => `${l.path} (${l.owner})`).join(", ")
3073
+ ] }) : null,
3074
+ /* @__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" }) })
3075
+ ] });
3076
+ }
3077
+ function TransportSection({ room, store }) {
3078
+ const a = store.actions;
3079
+ const [busy, setBusy] = (0, import_react.useState)(null);
3080
+ const cur = room.transport === "local" ? "local" : room.transport === "lan" ? "relay" : "relay";
3081
+ const go = async (target) => {
3082
+ setBusy(target);
3083
+ try {
3084
+ await a.setTransport(room.roomId, target);
3085
+ 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");
3086
+ } catch (e) {
3087
+ a.toast(e.message);
3088
+ } finally {
3089
+ setBusy(null);
3090
+ }
3091
+ };
3092
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-detail-section", "data-rm-transport": "", children: [
3093
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-detail-h", children: "Transport" }),
3094
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-mode-row", children: [
3095
+ /* @__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" }),
3096
+ /* @__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)" }),
3097
+ /* @__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" })
3098
+ ] }),
3099
+ /* @__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." })
3100
+ ] });
3101
+ }
3102
+ function AgentDialog({ store, d }) {
3103
+ const a = store.actions;
3104
+ const s = useStore(store);
3105
+ const room = (s.rooms || []).find((r) => r.roomId === d.roomId);
3106
+ const existing = room && room.local ? room.local.agents.find((x) => x.agentId === d.agentId) : null;
3107
+ const [presets, setPresets] = (0, import_react.useState)([]);
3108
+ const [f, setF] = (0, import_react.useState)(() => existing ? { 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 } : { preset: "", name: "", persona: "", systemPrompt: "", model: "", reasoningEffort: "medium", scope: "read", approvalRequired: false, moderator: false });
3109
+ const [err, setErr] = (0, import_react.useState)(null);
3110
+ const [busy, setBusy] = (0, import_react.useState)(false);
3111
+ (0, import_react.useEffect)(() => {
3112
+ a.presets().then(setPresets).catch(() => {
3113
+ });
3114
+ }, []);
3115
+ 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" });
3116
+ const submit = async () => {
3117
+ if (!f.name.trim()) {
3118
+ setErr("Name required");
3119
+ return;
3120
+ }
3121
+ setBusy(true);
3122
+ setErr(null);
3123
+ try {
3124
+ const body = { name: f.name, persona: f.persona, systemPrompt: f.systemPrompt, model: f.model, reasoningEffort: f.reasoningEffort, scope: f.scope, approvalRequired: f.approvalRequired, moderator: f.moderator, preset: f.preset || void 0 };
3125
+ if (existing) await a.updateAgent(d.roomId, existing.agentId, body);
3126
+ else {
3127
+ const r = await a.addAgent(d.roomId, body);
3128
+ a.toast(`${r.name} joined as @${r.agentId}`);
3129
+ }
3130
+ a.closeDialog();
3131
+ } catch (e) {
3132
+ setErr(e.message);
3133
+ setBusy(false);
3134
+ }
3135
+ };
3136
+ const remove = async () => {
3137
+ setBusy(true);
3138
+ try {
3139
+ const r = await a.removeAgent(d.roomId, existing.agentId);
3140
+ a.toast(`Removed ${existing.name}${r.branchKept ? ` \xB7 branch ${r.branchKept} kept` : ""}`);
3141
+ a.closeDialog();
3142
+ } catch (e) {
3143
+ setErr(e.message);
3144
+ setBusy(false);
3145
+ }
3146
+ };
3147
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3148
+ import_dsh_client_ui_primitives.Modal,
3149
+ {
3150
+ open: true,
3151
+ onClose: a.closeDialog,
3152
+ title: existing ? `Edit ${existing.name}` : "Add local agent",
3153
+ closeLabel: "Close",
3154
+ className: "rm-modal-wide",
3155
+ description: existing ? `Member @${existing.agentId} \xB7 own identity and dsh session in this room's workspace.` : "Runs on this dsh as a separate room member: own keypair, own session, own scope. Remote peers see it as a member.",
3156
+ footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
3157
+ 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,
3158
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { flex: 1 } }),
3159
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Cancel" }),
3160
+ /* @__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" })
3161
+ ] }),
3162
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-root rm-form rm-form-wide", "data-rm-agent-form": "", children: [
3163
+ !existing ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-span-2", children: [
3164
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Preset" }),
3165
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-preset-row", children: 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: [
3166
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: p.name }),
3167
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: p.persona })
3168
+ ] }, p.key)) })
3169
+ ] }) : null,
3170
+ /* @__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) => setF({ ...f, name: e.target.value }), placeholder: "e.g. Coder" }) }),
3171
+ /* @__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 }), placeholder: "e.g. Implements changes" }) }),
3172
+ /* @__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 }) }) }) }),
3173
+ /* @__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: [
3174
+ MODEL_CHOICES.map((m) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: m, children: m || "dsh default model" }, m)),
3175
+ f.model && !MODEL_CHOICES.includes(f.model) ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: f.model, children: f.model }) : null
3176
+ ] }) }),
3177
+ /* @__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)) }) }),
3178
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { label: "Workspace scope", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-mode-row", children: [
3179
+ /* @__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" }),
3180
+ /* @__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" })
3181
+ ] }) }),
3182
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-col", children: [
3183
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-row", children: [
3184
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
3185
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Approval required" }),
3186
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "Each run waits for your OK." })
3187
+ ] }),
3188
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: f.approvalRequired, onChange: (v) => setF({ ...f, approvalRequired: v }), label: "Approval required" })
3189
+ ] }),
3190
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-row", children: [
3191
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
3192
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Moderator" }),
3193
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "Splits requests into tasks and summarizes. One per room." })
3194
+ ] }),
3195
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: f.moderator, onChange: (v) => setF({ ...f, moderator: v }), label: "Moderator" })
3196
+ ] })
3197
+ ] }),
3198
+ 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,
3199
+ err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2 rm-error", children: err }) : null
3200
+ ] })
3201
+ }
3202
+ );
3203
+ }
3204
+ function DiffView({ patch }) {
3205
+ if (!patch) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "No changes." });
3206
+ 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)) });
3207
+ }
3208
+ function MergeDialog({ store, d }) {
3209
+ const a = store.actions;
3210
+ const [data, setData] = (0, import_react.useState)(null);
3211
+ const [sel, setSel] = (0, import_react.useState)(null);
3212
+ const [confirm, setConfirm] = (0, import_react.useState)(null);
3213
+ const [busy, setBusy] = (0, import_react.useState)(false);
3214
+ const [result, setResult] = (0, import_react.useState)(null);
3215
+ const load = async () => {
3216
+ try {
3217
+ const r = await a.diffs(d.roomId);
3218
+ setData(r);
3219
+ setSel((cur) => cur && r.agents.some((x) => x.agentId === cur) ? cur : (r.agents.find((x) => x.ahead) || r.agents[0] || {}).agentId || null);
3220
+ } catch (e) {
3221
+ setData({ error: e.message, agents: [] });
3222
+ }
3223
+ };
3224
+ (0, import_react.useEffect)(() => {
3225
+ void load();
3226
+ }, [d.roomId]);
3227
+ const ag = data && data.agents.find((x) => x.agentId === sel);
3228
+ const run = async (kind) => {
3229
+ setBusy(true);
3230
+ setResult(null);
3231
+ try {
3232
+ const r = kind === "merge" ? await a.merge(d.roomId, sel) : await a.discard(d.roomId, sel);
3233
+ 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." });
3234
+ await load();
3235
+ } catch (e) {
3236
+ setResult({ ok: false, text: e.message });
3237
+ } finally {
3238
+ setBusy(false);
3239
+ setConfirm(null);
3240
+ }
3241
+ };
3242
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3243
+ import_dsh_client_ui_primitives.Modal,
3244
+ {
3245
+ open: true,
3246
+ onClose: a.closeDialog,
3247
+ title: "Review & merge agent changes",
3248
+ closeLabel: "Close",
3249
+ className: "rm-modal-wide rm-modal-xl",
3250
+ 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.",
3251
+ footer: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", onClick: a.closeDialog, children: "Close" }),
3252
+ 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: [
3253
+ /* @__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: () => {
3254
+ setSel(x.agentId);
3255
+ setConfirm(null);
3256
+ setResult(null);
3257
+ }, children: [
3258
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Avatar, { name: x.name, size: 24 }),
3259
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-merge-agent-body", children: [
3260
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: x.name }),
3261
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-mono", children: x.branch || (x.isolation === "locks" ? "shared workspace \xB7 locks" : "no worktree yet") })
3262
+ ] }),
3263
+ 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: [
3264
+ x.files.length,
3265
+ " file",
3266
+ x.files.length === 1 ? "" : "s"
3267
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Tag, { tone: "quiet", children: "clean" })
3268
+ ] }, x.agentId)) }),
3269
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-merge-main", children: ag ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
3270
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-merge-head", children: [
3271
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
3272
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: ag.name }),
3273
+ " ",
3274
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-mono", children: ag.branch || "" }),
3275
+ ag.ahead ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-note", style: { margin: "0 0 0 8px" }, children: [
3276
+ ag.ahead,
3277
+ " commit(s) ahead"
3278
+ ] }) : null
3279
+ ] }),
3280
+ /* @__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: [
3281
+ /* @__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?` }),
3282
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { size: "sm", variant: "outline", onClick: () => setConfirm(null), children: "Cancel" }),
3283
+ /* @__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" })
3284
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
3285
+ /* @__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" }),
3286
+ /* @__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" })
3287
+ ] }) })
3288
+ ] }),
3289
+ result ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: result.ok ? "rm-ok" : "rm-error", children: result.text }) : null,
3290
+ ag.conflicts && ag.conflicts.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-error", children: [
3291
+ "Would conflict with the main workspace: ",
3292
+ ag.conflicts.join(", "),
3293
+ " \u2014 resolve on the branch or discard."
3294
+ ] }) : null,
3295
+ ag.mainDirty && ag.mainDirty.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-error", children: [
3296
+ "Uncommitted changes in the main workspace touch: ",
3297
+ ag.mainDirty.join(", ")
3298
+ ] }) : null,
3299
+ 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: [
3300
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-mono", children: f.path }),
3301
+ " ",
3302
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-diff-addn", children: [
3303
+ "+",
3304
+ f.adds
3305
+ ] }),
3306
+ " ",
3307
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-diff-deln", children: [
3308
+ "\u2212",
3309
+ f.dels
3310
+ ] })
3311
+ ] }, f.path)) }) : null,
3312
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DiffView, { patch: ag.patch }),
3313
+ ag.truncated ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "rm-note", children: "Diff truncated." }) : null
3314
+ ] }) : null })
3315
+ ] }) })
3316
+ }
3317
+ );
3318
+ }
2370
3319
  function EmptyMain({ store }) {
2371
3320
  const a = store.actions;
2372
3321
  const workspaces = useNativeWorkspaces(store);
@@ -2426,11 +3375,25 @@ function RoomsMain({ store }) {
2426
3375
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-chat-header-title", children: room.name }),
2427
3376
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-chat-header-sub", children: [
2428
3377
  room.members.filter((m) => m.online !== false).length,
2429
- " online \xB7 ",
3378
+ " online",
3379
+ room.local && room.local.agents.length ? ` \xB7 ${room.local.agents.length} local agent${room.local.agents.length === 1 ? "" : "s"}` : "",
3380
+ " \xB7 ",
2430
3381
  room.public ? "public" : "private",
2431
3382
  room.status !== "online" ? ` \xB7 room ${room.status}` : ""
2432
3383
  ] })
2433
3384
  ] }),
3385
+ (() => {
3386
+ const act = (room.runs || []).filter((r) => r.state === "running" || r.state === "queued" || r.state === "approval");
3387
+ if (!act.length) return null;
3388
+ const running = act.filter((r) => r.state === "running").length;
3389
+ 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: [
3390
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StopIcon, {}),
3391
+ "Stop \xB7 ",
3392
+ running,
3393
+ " running",
3394
+ act.length > running ? `, ${act.length - running} queued` : ""
3395
+ ] });
3396
+ })(),
2434
3397
  room.public ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "rm-badge rm-badge-public", children: [
2435
3398
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GlobeIcon, {}),
2436
3399
  "Public"
@@ -2438,10 +3401,10 @@ function RoomsMain({ store }) {
2438
3401
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LockIcon, {}),
2439
3402
  "Private"
2440
3403
  ] }),
2441
- room.transport === "lan" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "rm-badge", children: "LAN/VPS" }) : null,
3404
+ 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
3405
  /* @__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
3406
  ] }),
2444
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChatBody, { messages }),
3407
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChatBody, { messages, members: room.members }),
2445
3408
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Composer, { room, store, draft, setDraft })
2446
3409
  ] }),
2447
3410
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -2471,7 +3434,7 @@ function Field({ label, hint, children, htmlFor }) {
2471
3434
  function CreateDialog({ store, d }) {
2472
3435
  const s = useStore(store);
2473
3436
  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" });
3437
+ const [f, setF] = (0, import_react.useState)({ name: "", description: "", tags: "", public: false, mode: "lan", lanHostPort: "62.234.113.56:80", localOnly: false, agents: [] });
2475
3438
  const [err, setErr] = (0, import_react.useState)(null);
2476
3439
  const [busy, setBusy] = (0, import_react.useState)(false);
2477
3440
  const [lanPeers, setLanPeers] = (0, import_react.useState)([]);
@@ -2494,7 +3457,9 @@ function CreateDialog({ store, d }) {
2494
3457
  mode: f.mode,
2495
3458
  lanHostPort: f.mode === "lan" ? f.lanHostPort.trim() : void 0,
2496
3459
  workspacePath: ws.path,
2497
- workspaceName: ws.title
3460
+ workspaceName: ws.title,
3461
+ localOnly: f.localOnly,
3462
+ agents: f.agents
2498
3463
  });
2499
3464
  } catch (e) {
2500
3465
  setErr(e.message);
@@ -2544,12 +3509,29 @@ function CreateDialog({ store, d }) {
2544
3509
  ] }, p.hostPort))
2545
3510
  ] })
2546
3511
  ] }) }) : null,
3512
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-span-2 rm-local-box", children: [
3513
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-switch-row", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
3514
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Local agents" }),
3515
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "Host several agents from this dsh as separate members (own identity, session, scope). Edit them later in room details." })
3516
+ ] }) }),
3517
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-preset-row", children: [["moderator", "Moderator"], ["architect", "Architect"], ["coder", "Coder"], ["reviewer", "Reviewer"]].map(([k, label]) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "rm-preset rm-preset-check" + (f.agents.includes(k) ? " is-on" : ""), children: [
3518
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "checkbox", checked: f.agents.includes(k), onChange: (e) => setF({ ...f, agents: e.target.checked ? [...f.agents, k] : f.agents.filter((x) => x !== k) }) }),
3519
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: label })
3520
+ ] }, k)) }),
3521
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-switch-row", children: [
3522
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
3523
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "Local only (no network)" }),
3524
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-hint", children: "In-process loopback \u2014 nothing leaves this machine. One click to switch to Relay/LAN later." })
3525
+ ] }),
3526
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: f.localOnly, onChange: (v) => setF({ ...f, localOnly: v, public: v ? false : f.public }), label: "Local only" })
3527
+ ] })
3528
+ ] }),
2547
3529
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "rm-span-2 rm-switch-row", children: [
2548
3530
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
2549
3531
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-field-label", children: "List in public directory" }),
2550
3532
  /* @__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." })
2551
3533
  ] }),
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" })
3534
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Switch, { checked: f.public, onChange: (v) => setF({ ...f, public: v, localOnly: v ? false : f.localOnly }), label: "List in public directory" })
2553
3535
  ] }),
2554
3536
  err ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "rm-span-2 rm-error", children: err }) : null
2555
3537
  ] })
@@ -2891,6 +3873,8 @@ function Overlay({ store }) {
2891
3873
  d?.kind === "config" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConfigDialog, { store, d }) : null,
2892
3874
  d?.kind === "workspace" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WorkspaceDialog, { store }) : null,
2893
3875
  d?.kind === "browse" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BrowseDialog, { store }) : null,
3876
+ d?.kind === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AgentDialog, { store, d }) : null,
3877
+ d?.kind === "merge" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MergeDialog, { store, d }) : null,
2894
3878
  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
3879
  ] });
2896
3880
  }