conductor-remote 1.16.1 → 1.18.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.
@@ -0,0 +1 @@
1
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, -apple-system, "SF Pro Text", "Segoe UI", Roboto, sans-serif;--font-mono:ui-monospace, "SF Mono", "JetBrains Mono", "Fira Code", Menlo, monospace;--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--container-sm:24rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-wide:.025em;--leading-tight:1.25;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--radius-3xl:1.5rem;--ease-out:cubic-bezier(0, 0, .2, 1);--animate-spin:spin 1s linear infinite;--blur-xl:24px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-bg:#0a0b0e;--color-surface:#14161b;--color-surface-2:#1b1e26;--color-border:#262a33;--color-border-soft:#1e222a;--color-text:#e7e9ee;--color-muted:#8b909d;--color-faint:#5c616d;--color-accent:#8b7dff;--color-accent-soft:#2a2650;--color-working:#f5a623;--color-idle:#3ecf8e;--color-done:#5b9dff;--color-pr-merged:#ac47ff;--color-pr-draft:#a4a3a2;--color-pr-conflicts:#fd9a00;--color-pr-mergeable:#49de80;--color-add:#3ecf8e;--color-del:#ff6b6b}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}*{-webkit-tap-highlight-color:transparent}html,body,#root{height:100dvh}html{background:var(--color-bg)}body{background:var(--color-bg);color:var(--color-text);font-family:var(--font-sans);-webkit-font-smoothing:antialiased;overscroll-behavior:none;margin:0;overflow-x:hidden}button{font:inherit;color:inherit;cursor:pointer}::-webkit-scrollbar{width:0;height:0}}@layer components{.card{align-items:center;gap:calc(var(--spacing) * 3);border-radius:var(--radius-2xl);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-border);background-color:var(--color-surface);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3.5);text-align:left;transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));display:flex}.card:active{background-color:var(--color-surface-2);scale:.985}.pill{padding-inline:calc(var(--spacing) * 3.5);padding-block:calc(var(--spacing) * 1.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-muted);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));border-radius:3.40282e38px}.pill-active{background-color:var(--color-surface-2);color:var(--color-text)}.dot{width:calc(var(--spacing) * 2.5);height:calc(var(--spacing) * 2.5);background-color:var(--color-faint);--pulse-color:var(--color-working);border-radius:3.40282e38px;flex-shrink:0}.dot-pulse{box-shadow:0 0 0 0 var(--pulse-color)}@supports (color:color-mix(in lab,red,red)){.dot-pulse{box-shadow:0 0 color-mix(in srgb,var(--pulse-color) 60%,transparent)}}.dot-pulse{animation:1.6s ease-out infinite pulse}.dot-working{background:var(--color-working);box-shadow:0 0 #f5a62399}@supports (color:color-mix(in lab,red,red)){.dot-working{box-shadow:0 0 color-mix(in srgb,var(--color-working) 60%,transparent)}}.dot-working{animation:1.6s ease-out infinite pulse}.dot-idle{background:var(--color-idle)}.dot-done{background:var(--color-done)}.md>:first-child{margin-top:0}.md>:last-child{margin-bottom:0}.md p{margin:.5em 0}.md h1,.md h2,.md h3,.md h4,.md h5,.md h6{margin:.9em 0 .4em;font-weight:600;line-height:1.3}.md h1{font-size:1.25em}.md h2{font-size:1.15em}.md h3{font-size:1.05em}.md ul,.md ol{margin:.5em 0;padding-left:1.4em}.md ul{list-style:outside}.md ol{list-style:decimal}.md li,.md li>ul,.md li>ol{margin:.2em 0}.md code{background:#1b1e26cc;border-radius:.3rem}@supports (color:color-mix(in lab,red,red)){.md code{background:color-mix(in srgb,var(--color-surface-2) 80%,transparent)}}.md code{font-family:var(--font-mono);padding:.1em .35em;font-size:.85em}.md pre{border:1px solid var(--color-border-soft);background:var(--color-bg);border-radius:.6rem;margin:.5em 0;padding:.6em .75em;overflow-x:auto}.md pre code{background:0 0;padding:0;font-size:12px;line-height:1.5}.md a{color:var(--color-accent);text-underline-offset:2px;text-decoration:underline}.md blockquote{border-left:2px solid var(--color-border);color:var(--color-muted);margin:.5em 0;padding-left:.75em}.md hr{border:0;border-top:1px solid var(--color-border-soft);margin:.75em 0}.md table{border-collapse:collapse;max-width:100%;margin:.5em 0;font-size:.9em;display:block;overflow-x:auto}.md th,.md td{border:1px solid var(--color-border);text-align:left;padding:.3em .6em}.md th{background:var(--color-surface-2);font-weight:600}.md strong{font-weight:600}.md input[type=checkbox]{margin-right:.4em}}@layer utilities{.pointer-events-none{pointer-events:none}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:0}.inset-x-0{inset-inline:0}.inset-y-0{inset-block:0}.top-0{top:0}.top-1\.5{top:calc(var(--spacing) * 1.5)}.top-full{top:100%}.-right-0\.5{right:calc(var(--spacing) * -.5)}.right-1\.5{right:calc(var(--spacing) * 1.5)}.right-2{right:calc(var(--spacing) * 2)}.-bottom-0\.5{bottom:calc(var(--spacing) * -.5)}.bottom-0{bottom:0}.left-0{left:0}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.z-\[60\]{z-index:60}.mx-auto{margin-inline:auto}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.-mr-1{margin-right:calc(var(--spacing) * -1)}.mr-1{margin-right:var(--spacing)}.mb-0\.5{margin-bottom:calc(var(--spacing) * .5)}.mb-1{margin-bottom:var(--spacing)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.-ml-1{margin-left:calc(var(--spacing) * -1)}.ml-auto{margin-left:auto}.line-clamp-4{-webkit-line-clamp:4;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.size-1\.5{width:calc(var(--spacing) * 1.5);height:calc(var(--spacing) * 1.5)}.size-1\.5\!{width:calc(var(--spacing) * 1.5)!important;height:calc(var(--spacing) * 1.5)!important}.size-2{width:calc(var(--spacing) * 2);height:calc(var(--spacing) * 2)}.size-4{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.size-7{width:calc(var(--spacing) * 7);height:calc(var(--spacing) * 7)}.size-8{width:calc(var(--spacing) * 8);height:calc(var(--spacing) * 8)}.size-9{width:calc(var(--spacing) * 9);height:calc(var(--spacing) * 9)}.size-20{width:calc(var(--spacing) * 20);height:calc(var(--spacing) * 20)}.size-60{width:calc(var(--spacing) * 60);height:calc(var(--spacing) * 60)}.size-full{width:100%;height:100%}.h-full{height:100%}.max-h-40{max-height:calc(var(--spacing) * 40)}.w-1{width:var(--spacing)}.w-64{width:calc(var(--spacing) * 64)}.w-\[85\%\]{width:85%}.w-full{width:100%}.max-w-36{max-width:calc(var(--spacing) * 36)}.max-w-80{max-width:calc(var(--spacing) * 80)}.max-w-\[70vw\]{max-width:70vw}.max-w-\[85\%\]{max-width:85%}.max-w-\[92\%\]{max-width:92%}.max-w-full{max-width:100%}.max-w-sm{max-width:var(--container-sm)}.max-w-xs{max-width:var(--container-xs)}.min-w-0{min-width:0}.min-w-5{min-width:calc(var(--spacing) * 5)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.-translate-x-full{--tw-translate-x:-100%;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-0{--tw-translate-x:0;translate:var(--tw-translate-x) var(--tw-translate-y)}.-rotate-90{rotate:-90deg}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-spin{animation:var(--animate-spin)}.cursor-pointer{cursor:pointer}.resize-none{resize:none}.list-none{list-style-type:none}.flex-col{flex-direction:column}.place-items-center{place-items:center}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}.self-start{align-self:flex-start}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-3xl{border-radius:var(--radius-3xl)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.rounded-t-3xl{border-top-left-radius:var(--radius-3xl);border-top-right-radius:var(--radius-3xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-accent\/50{border-color:#8b7dff80}@supports (color:color-mix(in lab,red,red)){.border-accent\/50{border-color:color-mix(in oklab,var(--color-accent) 50%,transparent)}}.border-border{border-color:var(--color-border)}.border-border-soft{border-color:var(--color-border-soft)}.border-del\/30{border-color:#ff6b6b4d}@supports (color:color-mix(in lab,red,red)){.border-del\/30{border-color:color-mix(in oklab,var(--color-del) 30%,transparent)}}.border-faint{border-color:var(--color-faint)}.border-white\/80{border-color:#fffc}@supports (color:color-mix(in lab,red,red)){.border-white\/80{border-color:color-mix(in oklab,var(--color-white) 80%,transparent)}}.border-t-accent{border-top-color:var(--color-accent)}.bg-accent{background-color:var(--color-accent)}.bg-accent-soft{background-color:var(--color-accent-soft)}.bg-add{background-color:var(--color-add)}.bg-add\/10{background-color:#3ecf8e1a}@supports (color:color-mix(in lab,red,red)){.bg-add\/10{background-color:color-mix(in oklab,var(--color-add) 10%,transparent)}}.bg-bg{background-color:var(--color-bg)}.bg-bg\/80{background-color:#0a0b0ecc}@supports (color:color-mix(in lab,red,red)){.bg-bg\/80{background-color:color-mix(in oklab,var(--color-bg) 80%,transparent)}}.bg-black{background-color:var(--color-black)}.bg-black\/50{background-color:#00000080}@supports (color:color-mix(in lab,red,red)){.bg-black\/50{background-color:color-mix(in oklab,var(--color-black) 50%,transparent)}}.bg-black\/60{background-color:#0009}@supports (color:color-mix(in lab,red,red)){.bg-black\/60{background-color:color-mix(in oklab,var(--color-black) 60%,transparent)}}.bg-black\/75{background-color:#000000bf}@supports (color:color-mix(in lab,red,red)){.bg-black\/75{background-color:color-mix(in oklab,var(--color-black) 75%,transparent)}}.bg-del\/5{background-color:#ff6b6b0d}@supports (color:color-mix(in lab,red,red)){.bg-del\/5{background-color:color-mix(in oklab,var(--color-del) 5%,transparent)}}.bg-del\/10{background-color:#ff6b6b1a}@supports (color:color-mix(in lab,red,red)){.bg-del\/10{background-color:color-mix(in oklab,var(--color-del) 10%,transparent)}}.bg-idle\/10{background-color:#3ecf8e1a}@supports (color:color-mix(in lab,red,red)){.bg-idle\/10{background-color:color-mix(in oklab,var(--color-idle) 10%,transparent)}}.bg-surface{background-color:var(--color-surface)}.bg-surface-2{background-color:var(--color-surface-2)}.bg-surface\/60{background-color:#14161b99}@supports (color:color-mix(in lab,red,red)){.bg-surface\/60{background-color:color-mix(in oklab,var(--color-surface) 60%,transparent)}}.bg-surface\/95{background-color:#14161bf2}@supports (color:color-mix(in lab,red,red)){.bg-surface\/95{background-color:color-mix(in oklab,var(--color-surface) 95%,transparent)}}.bg-transparent{background-color:#0000}.bg-white{background-color:var(--color-white)}.bg-working{background-color:var(--color-working)}.bg-working\/10{background-color:#f5a6231a}@supports (color:color-mix(in lab,red,red)){.bg-working\/10{background-color:color-mix(in oklab,var(--color-working) 10%,transparent)}}.object-contain{object-fit:contain}.object-cover{object-fit:cover}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-6{padding:calc(var(--spacing) * 6)}.px-1{padding-inline:var(--spacing)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.px-8{padding-inline:calc(var(--spacing) * 8)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-16{padding-block:calc(var(--spacing) * 16)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-2\.5{padding-bottom:calc(var(--spacing) * 2.5)}.pl-2{padding-left:calc(var(--spacing) * 2)}.pl-3{padding-left:calc(var(--spacing) * 3)}.text-center{text-align:center}.font-mono{font-family:var(--font-mono)}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.text-\[11\.5px\]{font-size:11.5px}.text-\[11px\]{font-size:11px}.text-\[12\.5px\]{font-size:12.5px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.text-\[14px\]{font-size:14px}.text-\[15px\]{font-size:15px}.leading-\[1\.5\]{--tw-leading:1.5;line-height:1.5}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.\[overflow-wrap\:anywhere\]{overflow-wrap:anywhere}.whitespace-nowrap{white-space:nowrap}.whitespace-pre{white-space:pre}.whitespace-pre-wrap{white-space:pre-wrap}.text-accent{color:var(--color-accent)}.text-add{color:var(--color-add)}.text-black{color:var(--color-black)}.text-del{color:var(--color-del)}.text-del\/80{color:#ff6b6bcc}@supports (color:color-mix(in lab,red,red)){.text-del\/80{color:color-mix(in oklab,var(--color-del) 80%,transparent)}}.text-faint{color:var(--color-faint)}.text-idle{color:var(--color-idle)}.text-muted{color:var(--color-muted)}.text-text{color:var(--color-text)}.text-white{color:var(--color-white)}.text-working{color:var(--color-working)}.uppercase{text-transform:uppercase}.italic{font-style:italic}.underline-offset-2{text-underline-offset:2px}.\[color-scheme\:dark\]{color-scheme:dark}.opacity-60{opacity:.6}.shadow-\[0_0_0_100vmax_rgba\(0\,0\,0\,0\.5\)\]{--tw-shadow:0 0 0 100vmax var(--tw-shadow-color,#00000080);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-surface{--tw-ring-color:var(--color-surface)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-blur{--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-blur-xl{--tw-backdrop-blur:blur(var(--blur-xl));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-200{--tw-duration:.2s;transition-duration:.2s}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.group-open\:rotate-90:is(:where(.group):is([open],:popover-open,:open) *){rotate:90deg}.placeholder\:font-sans::placeholder{font-family:var(--font-sans)}.placeholder\:text-faint::placeholder{color:var(--color-faint)}.focus-within\:border-accent\/60:focus-within{border-color:#8b7dff99}@supports (color:color-mix(in lab,red,red)){.focus-within\:border-accent\/60:focus-within{border-color:color-mix(in oklab,var(--color-accent) 60%,transparent)}}@media(hover:hover){.hover\:underline:hover{text-decoration-line:underline}}.active\:scale-90:active{--tw-scale-x:90%;--tw-scale-y:90%;--tw-scale-z:90%;scale:var(--tw-scale-x) var(--tw-scale-y)}.active\:scale-95:active{--tw-scale-x:95%;--tw-scale-y:95%;--tw-scale-z:95%;scale:var(--tw-scale-x) var(--tw-scale-y)}.active\:bg-black\/70:active{background-color:#000000b3}@supports (color:color-mix(in lab,red,red)){.active\:bg-black\/70:active{background-color:color-mix(in oklab,var(--color-black) 70%,transparent)}}.active\:bg-surface:active{background-color:var(--color-surface)}.active\:bg-surface-2:active,.disabled\:bg-surface-2:disabled{background-color:var(--color-surface-2)}.disabled\:text-faint:disabled{color:var(--color-faint)}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-60:disabled{opacity:.6}@media(min-width:48rem){.md\:static{position:static}.md\:inset-0{inset:0}.md\:z-auto{z-index:auto}.md\:m-auto{margin:auto}.md\:block{display:block}.md\:hidden{display:none}.md\:h-fit{height:fit-content}.md\:w-72{width:calc(var(--spacing) * 72)}.md\:max-w-none{max-width:none}.md\:shrink-0{flex-shrink:0}.md\:translate-x-0{--tw-translate-x:0;translate:var(--tw-translate-x) var(--tw-translate-y)}.md\:rounded-3xl{border-radius:var(--radius-3xl)}.md\:transition-none{transition-property:none}}@media(min-width:64rem){.lg\:static{position:static}.lg\:z-auto{z-index:auto}.lg\:w-80{width:calc(var(--spacing) * 80)}.lg\:w-\[380px\]{width:380px}.lg\:shrink-0{flex-shrink:0}.lg\:border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.lg\:border-border-soft{border-color:var(--color-border-soft)}}@media(min-width:80rem){.xl\:w-\[460px\]{width:460px}}.\[\&\:\:-webkit-details-marker\]\:hidden::-webkit-details-marker{display:none}}@keyframes pulse{50%{opacity:.5}}@keyframes fade-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}.fade-in{animation:.18s ease-out fade-in}.pt-safe{padding-top:max(env(safe-area-inset-top),.5rem)}.pb-safe{padding-bottom:max(env(safe-area-inset-bottom),.5rem)}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@keyframes spin{to{transform:rotate(360deg)}}
package/dist/index.html CHANGED
@@ -11,8 +11,8 @@
11
11
  <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
12
12
  <link rel="icon" type="image/svg+xml" href="/icon.svg" />
13
13
  <title>Conductor Remote</title>
14
- <script type="module" crossorigin src="/assets/index-CkWIQoxg.js"></script>
15
- <link rel="stylesheet" crossorigin href="/assets/index-DLTj56Tj.css">
14
+ <script type="module" crossorigin src="/assets/index-BTmIqTa9.js"></script>
15
+ <link rel="stylesheet" crossorigin href="/assets/index-Dtq6pwjg.css">
16
16
  <link rel="manifest" href="/manifest.webmanifest"></head>
17
17
  <body>
18
18
  <div id="root"></div>
package/dist/sw.js CHANGED
@@ -1 +1 @@
1
- if(!self.define){let e,i={};const n=(n,s)=>(n=new URL(n+".js",s).href,i[n]||new Promise(i=>{if("document"in self){const e=document.createElement("script");e.src=n,e.onload=i,document.head.appendChild(e)}else e=n,importScripts(n),i()}).then(()=>{let e=i[n];if(!e)throw new Error(`Module ${n} didn’t register its module`);return e}));self.define=(s,o)=>{const r=e||("document"in self?document.currentScript.src:"")||location.href;if(i[r])return;let t={};const c=e=>n(e,r),l={module:{uri:r},exports:t,require:c};i[r]=Promise.all(s.map(e=>l[e]||c(e))).then(e=>(o(...e),t))}}define(["./workbox-9c191d2f"],function(e){"use strict";self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"index.html",revision:"352bf5ca64a8d1beba18dbca20b4ff27"},{url:"assets/workbox-window.prod.es5-BBnX5xw4.js",revision:null},{url:"assets/index-DLTj56Tj.css",revision:null},{url:"assets/index-CkWIQoxg.js",revision:null},{url:"apple-touch-icon.png",revision:"2b9301416b880d45d4bb655f2600d1f2"},{url:"icon-192.png",revision:"c5e01ac58768627e18ee7b8b6a9239ef"},{url:"icon-512.png",revision:"a40638c55e310312457a621c9a0002c8"},{url:"icon-maskable-512.png",revision:"a40638c55e310312457a621c9a0002c8"},{url:"icon.svg",revision:"c1aee186821798733dd477e69a0ef243"},{url:"manifest.webmanifest",revision:"cf88fbc5755108a7fe0616fa160a8a15"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("/index.html"),{denylist:[/^\/api\//]}))});
1
+ if(!self.define){let e,i={};const n=(n,s)=>(n=new URL(n+".js",s).href,i[n]||new Promise(i=>{if("document"in self){const e=document.createElement("script");e.src=n,e.onload=i,document.head.appendChild(e)}else e=n,importScripts(n),i()}).then(()=>{let e=i[n];if(!e)throw new Error(`Module ${n} didn’t register its module`);return e}));self.define=(s,o)=>{const r=e||("document"in self?document.currentScript.src:"")||location.href;if(i[r])return;let t={};const l=e=>n(e,r),c={module:{uri:r},exports:t,require:l};i[r]=Promise.all(s.map(e=>c[e]||l(e))).then(e=>(o(...e),t))}}define(["./workbox-9c191d2f"],function(e){"use strict";self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"index.html",revision:"f6f8ecb9ae2d872381f30361b5905ab7"},{url:"assets/workbox-window.prod.es5-BBnX5xw4.js",revision:null},{url:"assets/index-Dtq6pwjg.css",revision:null},{url:"assets/index-BTmIqTa9.js",revision:null},{url:"apple-touch-icon.png",revision:"2b9301416b880d45d4bb655f2600d1f2"},{url:"icon-192.png",revision:"c5e01ac58768627e18ee7b8b6a9239ef"},{url:"icon-512.png",revision:"a40638c55e310312457a621c9a0002c8"},{url:"icon-maskable-512.png",revision:"a40638c55e310312457a621c9a0002c8"},{url:"icon.svg",revision:"c1aee186821798733dd477e69a0ef243"},{url:"manifest.webmanifest",revision:"cf88fbc5755108a7fe0616fa160a8a15"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("/index.html"),{denylist:[/^\/api\//]}))});
@@ -94,5 +94,25 @@ export async function workspaceDiff(worktree, base) {
94
94
  const truncated = patch.length > MAX_PATCH_BYTES;
95
95
  if (truncated)
96
96
  patch = `${patch.slice(0, MAX_PATCH_BYTES)}\n\n… diff truncated (${patch.length} bytes) …`;
97
- return { base: ref, mergeBase, files, patch, truncated };
97
+ const { dirty, unpushed } = await localState(worktree);
98
+ return { base: ref, mergeBase, files, patch, truncated, dirty, unpushed };
99
+ }
100
+ /**
101
+ * Local publish state of the worktree: does it have uncommitted changes, and are
102
+ * there commits not yet on its remote-tracking branch? Together these drive the
103
+ * bar's "Commit & push" action (you must land local work before a PR reflects it).
104
+ */
105
+ async function localState(worktree) {
106
+ const status = await git(worktree, ['status', '--porcelain']).catch(() => '');
107
+ const dirty = status.trim() !== '';
108
+ let unpushed = false;
109
+ try {
110
+ // `@{upstream}` throws when no upstream is configured — then there's nothing to compare against.
111
+ const count = (await git(worktree, ['rev-list', '--count', '@{upstream}..HEAD'])).trim();
112
+ unpushed = Number(count) > 0;
113
+ }
114
+ catch {
115
+ unpushed = false;
116
+ }
117
+ return { dirty, unpushed };
98
118
  }
@@ -0,0 +1,53 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ const exec = promisify(execFile);
4
+ async function gh(root, args) {
5
+ const { stdout } = await exec('gh', args, { cwd: root, encoding: 'utf8', timeout: 30_000 });
6
+ return stdout;
7
+ }
8
+ /**
9
+ * The repo's merge method, preferring squash (this project's convention), then a
10
+ * merge commit, then rebase — whichever GitHub allows for the repo. Defaults to
11
+ * squash if the query fails.
12
+ */
13
+ async function preferredMethod(root) {
14
+ try {
15
+ const out = await gh(root, ['repo', 'view', '--json', 'squashMergeAllowed,mergeCommitAllowed,rebaseMergeAllowed']);
16
+ const a = JSON.parse(out);
17
+ if (a.squashMergeAllowed)
18
+ return 'squash';
19
+ if (a.mergeCommitAllowed)
20
+ return 'merge';
21
+ if (a.rebaseMergeAllowed)
22
+ return 'rebase';
23
+ }
24
+ catch {
25
+ // gh missing / not a GitHub repo → fall back; the merge call will surface any real error.
26
+ }
27
+ return 'squash';
28
+ }
29
+ /**
30
+ * Merge the workspace's open PR. `gh pr merge <branch>` resolves the PR from its
31
+ * head branch, so we need no PR number. We don't `--delete-branch` (the worktree
32
+ * still has it checked out; Conductor deletes it on archive, not on merge) and
33
+ * push nothing — GitHub performs the merge. Any GitHub-side refusal (checks,
34
+ * reviews, conflicts, draft) comes back as a loud error.
35
+ */
36
+ export async function mergePr(ws) {
37
+ const branch = ws.branch ?? '';
38
+ const root = ws.repo_root;
39
+ if (!branch)
40
+ return { ok: false, branch, error: 'workspace has no branch' };
41
+ if (!root)
42
+ return { ok: false, branch, error: 'repo root unresolved' };
43
+ const method = await preferredMethod(root);
44
+ try {
45
+ await gh(root, ['pr', 'merge', branch, `--${method}`]);
46
+ return { ok: true, branch, method };
47
+ }
48
+ catch (err) {
49
+ const stderr = err.stderr;
50
+ const message = (stderr || (err instanceof Error ? err.message : String(err))).trim();
51
+ return { ok: false, branch, method, error: message };
52
+ }
53
+ }
@@ -19,12 +19,16 @@ export function attachPrStatus(workspaces) {
19
19
  const repos = new Set();
20
20
  for (const w of workspaces) {
21
21
  w.pr_status = null;
22
+ w.pr_number = null;
23
+ w.pr_url = null;
22
24
  if (!w.branch || !w.repo_root)
23
25
  continue;
24
26
  repos.add(w.repo_root);
25
27
  const pr = repoCache.get(w.repo_root)?.prs?.get(w.branch);
26
28
  if (!pr)
27
29
  continue;
30
+ w.pr_number = pr.number;
31
+ w.pr_url = pr.url;
28
32
  if (pr.state === 'MERGED')
29
33
  w.pr_status = 'merged';
30
34
  else if (pr.state === 'OPEN') {
@@ -57,7 +61,7 @@ function refreshRepo(root) {
57
61
  /** One `gh` call per repo → its PRs keyed by head branch. Never rejects. */
58
62
  async function fetchRepoPRs(root) {
59
63
  try {
60
- const { stdout } = await exec('gh', ['pr', 'list', '--state', 'all', '--limit', '100', '--json', 'headRefName,state,isDraft,updatedAt'], { cwd: root, encoding: 'utf8', timeout: 15_000 });
64
+ const { stdout } = await exec('gh', ['pr', 'list', '--state', 'all', '--limit', '100', '--json', 'headRefName,number,url,state,isDraft,updatedAt'], { cwd: root, encoding: 'utf8', timeout: 15_000 });
61
65
  const list = JSON.parse(stdout);
62
66
  // Oldest first so the newest PR wins when a branch has been reused across PRs.
63
67
  list.sort((a, b) => a.updatedAt.localeCompare(b.updatedAt));
@@ -90,19 +90,36 @@ export class Reads {
90
90
  WHERE workspace_id = ? AND COALESCE(is_hidden, 0) = 0
91
91
  ORDER BY created_at ASC`, [workspaceId]);
92
92
  }
93
+ /** Session → worktree path, cached: it's stable for a session's lifetime and polled every tick. */
94
+ worktreeBySession = new Map();
95
+ sessionWorktree(sessionId) {
96
+ const cached = this.worktreeBySession.get(sessionId);
97
+ if (cached !== undefined)
98
+ return cached;
99
+ const rows = this.db.query(`SELECT w.directory_name, w.branch, r.name AS repo_name, r.root_path AS repo_root
100
+ FROM sessions s
101
+ JOIN workspaces w ON w.id = s.workspace_id
102
+ LEFT JOIN repos r ON r.id = w.repository_id
103
+ WHERE s.id = ? LIMIT 1`, [sessionId]);
104
+ const r = rows[0];
105
+ const worktree = r
106
+ ? resolveWorktree(this.workspacesRoot, r.repo_name, r.directory_name, r.branch, r.repo_root)
107
+ : null;
108
+ this.worktreeBySession.set(sessionId, worktree);
109
+ return worktree;
110
+ }
93
111
  /** Incremental transcript fetch. `afterRowid` is the cursor from a prior call. */
94
112
  getMessages(sessionId, afterRowid = 0) {
95
113
  const rows = this.db.query(`SELECT rowid, id, role, content, full_message, created_at, sent_at, queue_order
96
114
  FROM session_messages
97
115
  WHERE session_id = ? AND rowid > ?
98
116
  ORDER BY rowid ASC`, [sessionId, afterRowid]);
117
+ const worktree = this.sessionWorktree(sessionId);
99
118
  const entries = [];
100
119
  let cursor = afterRowid;
101
120
  for (const row of rows) {
102
121
  cursor = row.rowid;
103
- const entry = parseMessage(row);
104
- if (entry)
105
- entries.push(entry);
122
+ entries.push(...parseMessage(row, worktree));
106
123
  }
107
124
  return { entries, cursor };
108
125
  }
@@ -7,6 +7,7 @@ import { startAutoUpdate, updateStatus } from "./autoupdate.js";
7
7
  import { loadConfig } from "./config.js";
8
8
  import { ConductorDb } from "./db.js";
9
9
  import { workspaceDiff } from "./git.js";
10
+ import { mergePr } from "./merge.js";
10
11
  import { attachPrStatus } from "./pr.js";
11
12
  import { Reads } from "./reads.js";
12
13
  import { describeActuator, newChat, pickActuator } from "./writes.js";
@@ -175,6 +176,15 @@ const server = http.createServer(async (req, res) => {
175
176
  const diff = await workspaceDiff(ws.worktree, ws.baseBranch);
176
177
  return json(req, res, 200, diff);
177
178
  }
179
+ // POST /api/workspaces/:id/merge — merge the workspace's open PR (mirrors Conductor's merge button)
180
+ m = pathname.match(/^\/api\/workspaces\/([^/]+)\/merge$/);
181
+ if (req.method === 'POST' && m) {
182
+ const ws = reads.getWorkspace(decodeURIComponent(m[1]));
183
+ if (!ws)
184
+ return json(req, res, 404, { error: 'workspace not found' });
185
+ const result = await mergePr(ws);
186
+ return json(req, res, result.ok ? 200 : 409, result);
187
+ }
178
188
  // GET /api/sessions/:id/messages?after=<rowid>
179
189
  m = pathname.match(/^\/api\/sessions\/([^/]+)\/messages$/);
180
190
  if (req.method === 'GET' && m) {
@@ -1,36 +1,40 @@
1
1
  /**
2
2
  * Conductor stores each turn's raw Claude Code SDK stream JSON in
3
3
  * `session_messages.content`. User-typed prompts are stored as plain text.
4
- * This turns a row into a compact, phone-renderable entry.
4
+ * This turns a row into compact, phone-renderable entries.
5
+ *
6
+ * Classification rule (verified against the whole DB): a JSON frame with
7
+ * `type:"user"` is ALWAYS tool plumbing — every one of them carries
8
+ * tool_result blocks, never the user's own words. Real prompts are the
9
+ * plain-text rows. Never render an SDK user frame as a user bubble.
5
10
  */
6
- function textFromBlocks(blocks) {
7
- const parts = [];
8
- let tool;
9
- for (const b of blocks) {
10
- if (b.type === 'text' && b.text)
11
- parts.push(b.text);
12
- else if (b.type === 'thinking' && typeof b.text === 'string')
13
- parts.push(b.text);
14
- else if (b.type === 'tool_use') {
15
- tool = b.name;
16
- const inputStr = summarizeToolInput(b.input);
17
- parts.push(inputStr ? `▸ ${b.name}: ${inputStr}` : `▸ ${b.name}`);
18
- }
19
- else if (b.type === 'tool_result') {
20
- parts.push(summarizeToolResult(b.content));
21
- }
22
- }
23
- return { text: parts.join('\n').trim(), tool };
11
+ const clip = (s, n) => (s.length > n ? `${s.slice(0, n)}…` : s);
12
+ const str = (v) => (typeof v === 'string' && v.trim() ? v.trim() : undefined);
13
+ /** Make tool details repo-relative: absolute worktree paths waste the whole line on a phone. */
14
+ function stripWorktree(s, worktree) {
15
+ if (!worktree)
16
+ return s;
17
+ // Conductor prefixes commands with `cd <worktree>` (newline- or &&-joined) drop the whole clause.
18
+ if (s.startsWith(`cd ${worktree}`))
19
+ s = s.slice(`cd ${worktree}`.length).replace(/^\s*(&&)?\s*/, '');
20
+ return s.replaceAll(`${worktree}/`, '').replaceAll(worktree, '.');
24
21
  }
25
- function summarizeToolInput(input) {
22
+ /**
23
+ * Mirror Conductor's one-line tool rows: the human description as the title
24
+ * (Bash always has one), the primary input as a mono detail. Tools without a
25
+ * recognizable primary input get the title alone — dumping raw JSON is noise.
26
+ */
27
+ function summarizeToolUse(name, input, worktree) {
26
28
  if (!input || typeof input !== 'object')
27
- return '';
29
+ return { text: name };
28
30
  const o = input;
29
- const key = o.command ?? o.file_path ?? o.path ?? o.pattern ?? o.description ?? o.prompt;
30
- const s = typeof key === 'string' ? key : JSON.stringify(o);
31
- return s.length > 140 ? `${s.slice(0, 140)}…` : s;
31
+ const text = str(o.description) ?? name;
32
+ const detail = str(o.command) ?? str(o.file_path) ?? str(o.path) ?? str(o.pattern) ?? str(o.url) ?? str(o.skill) ?? str(o.prompt);
33
+ if (!detail || detail === text)
34
+ return { text };
35
+ return { text, detail: clip(stripWorktree(detail, worktree).replace(/\s+/g, ' '), 160) };
32
36
  }
33
- function summarizeToolResult(content) {
37
+ function resultText(content) {
34
38
  let s = '';
35
39
  if (typeof content === 'string')
36
40
  s = content;
@@ -39,45 +43,66 @@ function summarizeToolResult(content) {
39
43
  .map(c => (c && typeof c === 'object' && 'text' in c ? String(c.text) : ''))
40
44
  .join('');
41
45
  }
42
- s = s.trim();
43
- if (!s)
44
- return '↳ (result)';
45
- return `↳ ${s.length > 200 ? `${s.slice(0, 200)}…` : s}`;
46
+ return clip(s.replace(/<\/?tool_use_error>/g, '').trim(), 400);
46
47
  }
47
- export function parseMessage(row) {
48
+ export function parseMessage(row, worktree = null) {
48
49
  const queued = row.queue_order !== null && row.sent_at === null;
49
- const base = { id: row.id, rowid: row.rowid, ts: row.created_at, queued };
50
+ const base = { rowid: row.rowid, ts: row.created_at, queued };
50
51
  const content = row.content ?? '';
51
- // Plain user prompt (not SDK JSON).
52
+ // Plain user prompt (not SDK JSON) — the only source of real user bubbles.
52
53
  if (!content.startsWith('{')) {
53
54
  if (!content.trim())
54
- return null;
55
- return { ...base, role: 'user', text: content };
55
+ return [];
56
+ return [{ ...base, id: row.id, role: 'user', text: content }];
56
57
  }
57
58
  let parsed;
58
59
  try {
59
60
  parsed = JSON.parse(content);
60
61
  }
61
62
  catch {
62
- return { ...base, role: 'system', text: content.slice(0, 200) };
63
+ return [{ ...base, id: row.id, role: 'system', text: clip(content, 200) }];
63
64
  }
64
- // Skip pure bookkeeping frames (token accounting, etc.).
65
- if (parsed.type === 'system' && parsed.subtype === 'thinking_tokens')
66
- return null;
67
- if (parsed.type === 'result')
68
- return null;
65
+ // Bookkeeping frames: hooks, init, token accounting, end-of-turn results.
66
+ if (parsed.type === 'system' || parsed.type === 'result')
67
+ return [];
69
68
  const blocks = parsed.message?.content;
70
- if (Array.isArray(blocks)) {
71
- const { text, tool } = textFromBlocks(blocks);
72
- if (!text)
73
- return null;
74
- if (tool)
75
- return { ...base, role: 'tool', text, tool };
76
- if (parsed.type === 'user')
77
- return { ...base, role: 'user', text };
78
- return { ...base, role: 'assistant', text };
69
+ if (!Array.isArray(blocks)) {
70
+ if (parsed.type === 'user' || parsed.type === 'assistant')
71
+ return [];
72
+ // Unknown frame shape — keep a dim raw dump so Conductor drift stays visible.
73
+ return [{ ...base, id: row.id, role: 'system', text: clip(content, 200) }];
74
+ }
75
+ const entries = [];
76
+ const push = (e) => entries.push({ ...base, ...e, id: `${row.id}:${entries.length}` });
77
+ let pending = [];
78
+ const flush = () => {
79
+ const text = pending.join('\n').trim();
80
+ if (text)
81
+ push({ role: 'assistant', text });
82
+ pending = [];
83
+ };
84
+ for (const b of blocks) {
85
+ if (b.type === 'text' && typeof b.text === 'string') {
86
+ // Text inside an SDK user frame would be injected context, not the user.
87
+ if (parsed.type !== 'user')
88
+ pending.push(b.text);
89
+ }
90
+ else if (b.type === 'thinking') {
91
+ flush();
92
+ const text = str(b.thinking) ?? str(b.text);
93
+ if (text)
94
+ push({ role: 'thinking', text });
95
+ }
96
+ else if (b.type === 'tool_use' && typeof b.name === 'string') {
97
+ flush();
98
+ push({ role: 'tool', tool: b.name, ...summarizeToolUse(b.name, b.input, worktree) });
99
+ }
100
+ else if (b.type === 'tool_result' && b.is_error) {
101
+ // Successful results are noise on a phone; surface only failures.
102
+ flush();
103
+ push({ role: 'tool', error: true, text: resultText(b.content) || '(tool error)' });
104
+ }
79
105
  }
80
- if (parsed.type === 'system')
81
- return null;
82
- return { ...base, role: 'system', text: content.slice(0, 200) };
106
+ flush();
107
+ return entries;
83
108
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "conductor-remote",
3
- "version": "1.16.1",
3
+ "version": "1.18.0",
4
4
  "type": "module",
5
5
  "packageManager": "yarn@4.15.0",
6
6
  "description": "Phone control panel for local Conductor agents. Reads ride SQLite + git; prompts ride Conductor's own dispatch path.",