conductor-remote 1.53.4 → 1.54.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -6
- package/dist/assets/index-DjSimsDa.js +44 -0
- package/dist/assets/index-TXYaWW0Q.css +1 -0
- package/dist/index.html +2 -2
- package/dist/sw.js +1 -1
- package/dist-node/src/firstprompt.js +26 -3
- package/dist-node/src/mcp-tools.js +29 -9
- package/dist-node/src/model-cache.js +91 -0
- package/dist-node/src/routes.js +2 -0
- package/dist-node/src/server.js +33 -4
- package/dist-node/src/shared.js +44 -0
- package/package.json +3 -2
- package/dist/assets/index-CNdG7lFR.css +0 -1
- package/dist/assets/index-Dw55ffnp.js +0 -44
|
@@ -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-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction: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-outline-style:solid;--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;--container-md:28rem;--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-snug:1.375;--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:var(--app-height,100dvh)}html,body{overflow:hidden}@media(display-mode:standalone){html,body,#root{height:var(--app-height,100vh)}}html[data-standalone],html[data-standalone] body,html[data-standalone] #root{height:var(--app-height,100vh)}.app-height{height:var(--app-height,100dvh)}@media(display-mode:standalone){.app-height{height:var(--app-height,100vh)}}html[data-standalone] .app-height{height:var(--app-height,100vh)}@media(min-width:48rem){.app-height,html[data-standalone] .app-height{height:auto}}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}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)}.ctl{padding-inline:calc(var(--spacing) * 2);padding-block:var(--spacing);--tw-font-weight:var(--font-weight-medium);font-size:13px;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}.ctl-on{background-color:var(--color-surface-2);color:var(--color-text)}.ctl-staged{background-color:var(--color-accent-soft);color:var(--color-accent)}.dot{width:calc(var(--spacing) * 2.5);height:calc(var(--spacing) * 2.5);background-color:var(--color-faint);border-radius:3.40282e38px;flex-shrink:0}.dot-spinner{width:calc(var(--spacing) * 3.5);height:calc(var(--spacing) * 3.5);animation:var(--animate-spin);border-style:var(--tw-border-style);--spin-color:var(--color-working);border-width:2px;border-color:var(--spin-color);border-radius:3.40282e38px;flex-shrink:0}@supports (color:color-mix(in lab,red,red)){.dot-spinner{border-color:color-mix(in srgb,var(--spin-color) 30%,transparent)}}.dot-spinner{border-top-color:var(--spin-color);border-right-color:var(--spin-color)}.dot-idle{background:var(--color-idle)}.dot-done{background:var(--color-done)}.typing-dot{width:calc(var(--spacing) * 1.5);height:calc(var(--spacing) * 1.5);background-color:var(--color-muted);border-radius:3.40282e38px;animation:1.2s ease-in-out infinite typing}.typing-dot:nth-child(2){animation-delay:.15s}.typing-dot:nth-child(3){animation-delay:.3s}.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 img{border-radius:.6rem;max-width:100%;height:auto;margin:.5em 0;display:block}.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-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.invisible{visibility:hidden}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:0}.inset-1{inset:var(--spacing)}.inset-x-0{inset-inline:0}.inset-y-0{inset-block:0}.top-0{top:0}.top-0\.5{top:calc(var(--spacing) * .5)}.top-1\.5{top:calc(var(--spacing) * 1.5)}.top-1\/2{top:50%}.top-full{top:100%}.-right-0\.5{right:calc(var(--spacing) * -.5)}.right-1{right:var(--spacing)}.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}.bottom-full{bottom:100%}.left-0{left:0}.left-0\.5{left:calc(var(--spacing) * .5)}.left-3{left:calc(var(--spacing) * 3)}.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}.m-0{margin:0}.mx-auto{margin-inline:auto}.-mt-1{margin-top:calc(var(--spacing) * -1)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-2\.5{margin-top:calc(var(--spacing) * 2.5)}.mt-4{margin-top:calc(var(--spacing) * 4)}.-mr-1{margin-right:calc(var(--spacing) * -1)}.-mr-2{margin-right:calc(var(--spacing) * -2)}.mr-1{margin-right:var(--spacing)}.mr-1\.5{margin-right:calc(var(--spacing) * 1.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)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.-ml-1{margin-left:calc(var(--spacing) * -1)}.ml-1{margin-left:var(--spacing)}.ml-1\.5{margin-left:calc(var(--spacing) * 1.5)}.ml-auto{margin-left:auto}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.line-clamp-4{-webkit-line-clamp:4;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.size-1\.5{width:calc(var(--spacing) * 1.5);height:calc(var(--spacing) * 1.5)}.size-2{width:calc(var(--spacing) * 2);height:calc(var(--spacing) * 2)}.size-2\.5{width:calc(var(--spacing) * 2.5);height:calc(var(--spacing) * 2.5)}.size-3{width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.size-4{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.size-5{width:calc(var(--spacing) * 5);height:calc(var(--spacing) * 5)}.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-11{width:calc(var(--spacing) * 11);height:calc(var(--spacing) * 11)}.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-\[18px\]{width:18px;height:18px}.size-full{width:100%;height:100%}.h-4{height:calc(var(--spacing) * 4)}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-12{height:calc(var(--spacing) * 12)}.h-full{height:100%}.max-h-40{max-height:calc(var(--spacing) * 40)}.max-h-48{max-height:calc(var(--spacing) * 48)}.max-h-64{max-height:calc(var(--spacing) * 64)}.max-h-72{max-height:calc(var(--spacing) * 72)}.max-h-\[70\%\]{max-height:70%}.max-h-\[85dvh\]{max-height:85dvh}.min-h-0{min-height:0}.min-h-14{min-height:calc(var(--spacing) * 14)}.w-1{width:var(--spacing)}.w-5{width:calc(var(--spacing) * 5)}.w-11{width:calc(var(--spacing) * 11)}.w-12{width:calc(var(--spacing) * 12)}.w-44{width:calc(var(--spacing) * 44)}.w-56{width:calc(var(--spacing) * 56)}.w-60{width:calc(var(--spacing) * 60)}.w-64{width:calc(var(--spacing) * 64)}.w-\[85\%\]{width:85%}.w-full{width:100%}.w-px{width:1px}.max-w-36{max-width:calc(var(--spacing) * 36)}.max-w-40{max-width:calc(var(--spacing) * 40)}.max-w-64{max-width:calc(var(--spacing) * 64)}.max-w-80{max-width:calc(var(--spacing) * 80)}.max-w-\[55vw\]{max-width:55vw}.max-w-\[70vw\]{max-width:70vw}.max-w-\[85\%\]{max-width:85%}.max-w-\[85vw\]{max-width:85vw}.max-w-\[92\%\]{max-width:92%}.max-w-full{max-width:100%}.max-w-md{max-width:var(--container-md)}.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)}.min-w-max{min-width:max-content}.flex-1{flex:1}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.-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)}.translate-x-5{--tw-translate-x:calc(var(--spacing) * 5);translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-0{--tw-translate-y:0;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-y-3{--tw-translate-y:calc(var(--spacing) * 3);translate:var(--tw-translate-x) var(--tw-translate-y)}.-rotate-90{rotate:-90deg}.rotate-180{rotate:180deg}.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-default{cursor:default}.cursor-pointer{cursor:pointer}.resize{resize:both}.resize-none{resize:none}.scrollbar-thin{scrollbar-width:thin}.list-none{list-style-type:none}.grid-cols-\[auto_1fr\]{grid-template-columns:auto 1fr}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.items-stretch{align-items:stretch}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-start{justify-content:flex-start}.gap-0{gap:0}.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)}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}.self-start{align-self:flex-start}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.overscroll-contain{overscroll-behavior:contain}.rounded{border-radius:.25rem}.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-2xl{border-top-left-radius:var(--radius-2xl);border-top-right-radius:var(--radius-2xl)}.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-0{border-style:var(--tw-border-style);border-width:0}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-x{border-inline-style:var(--tw-border-style);border-inline-width:1px}.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{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-accent{border-color:var(--color-accent)}.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-del\/40{border-color:#ff6b6b66}@supports (color:color-mix(in lab,red,red)){.border-del\/40{border-color:color-mix(in oklab,var(--color-del) 40%,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)}.border-t-text{border-top-color:var(--color-text)}.border-l-accent{border-left-color:var(--color-accent)}.bg-accent{background-color:var(--color-accent)}.bg-accent-soft{background-color:var(--color-accent-soft)}.bg-accent-soft\/90{background-color:#2a2650e6}@supports (color:color-mix(in lab,red,red)){.bg-accent-soft\/90{background-color:color-mix(in oklab,var(--color-accent-soft) 90%,transparent)}}.bg-accent\/10{background-color:#8b7dff1a}@supports (color:color-mix(in lab,red,red)){.bg-accent\/10{background-color:color-mix(in oklab,var(--color-accent) 10%,transparent)}}.bg-accent\/25{background-color:#8b7dff40}@supports (color:color-mix(in lab,red,red)){.bg-accent\/25{background-color:color-mix(in oklab,var(--color-accent) 25%,transparent)}}.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-border-soft{background-color:var(--color-border-soft)}.bg-del{background-color:var(--color-del)}.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-del\/15{background-color:#ff6b6b26}@supports (color:color-mix(in lab,red,red)){.bg-del\/15{background-color:color-mix(in oklab,var(--color-del) 15%,transparent)}}.bg-pr-merged\/10{background-color:#ac47ff1a}@supports (color:color-mix(in lab,red,red)){.bg-pr-merged\/10{background-color:color-mix(in oklab,var(--color-pr-merged) 10%,transparent)}}.bg-pr-merged\/15{background-color:#ac47ff26}@supports (color:color-mix(in lab,red,red)){.bg-pr-merged\/15{background-color:color-mix(in oklab,var(--color-pr-merged) 15%,transparent)}}.bg-surface{background-color:var(--color-surface)}.bg-surface-2{background-color:var(--color-surface-2)}.bg-surface-2\/60{background-color:#1b1e2699}@supports (color:color-mix(in lab,red,red)){.bg-surface-2\/60{background-color:color-mix(in oklab,var(--color-surface-2) 60%,transparent)}}.bg-surface\/40{background-color:#14161b66}@supports (color:color-mix(in lab,red,red)){.bg-surface\/40{background-color:color-mix(in oklab,var(--color-surface) 40%,transparent)}}.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\/70{background-color:#14161bb3}@supports (color:color-mix(in lab,red,red)){.bg-surface\/70{background-color:color-mix(in oklab,var(--color-surface) 70%,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-0{padding:0}.p-1{padding:var(--spacing)}.p-2{padding:calc(var(--spacing) * 2)}.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-0\.5{padding-inline:calc(var(--spacing) * .5)}.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-4{padding-inline:calc(var(--spacing) * 4)}.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)}.py-px{padding-block:1px}.pt-0\.5{padding-top:calc(var(--spacing) * .5)}.pt-1{padding-top:var(--spacing)}.pt-1\.5{padding-top:calc(var(--spacing) * 1.5)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-2\.5{padding-top:calc(var(--spacing) * 2.5)}.pr-1{padding-right:var(--spacing)}.pr-9{padding-right:calc(var(--spacing) * 9)}.pb-0\.5{padding-bottom:calc(var(--spacing) * .5)}.pb-1{padding-bottom:var(--spacing)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-2\.5{padding-bottom:calc(var(--spacing) * 2.5)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pl-2{padding-left:calc(var(--spacing) * 2)}.pl-2\.5{padding-left:calc(var(--spacing) * 2.5)}.pl-3{padding-left:calc(var(--spacing) * 3)}.pl-8{padding-left:calc(var(--spacing) * 8)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.align-baseline{vertical-align:baseline}.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-\[0\.9em\]{font-size:.9em}.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-\[13\.5px\]{font-size:13.5px}.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-snug{--tw-leading:var(--leading-snug);line-height:var(--leading-snug)}.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}.break-words{overflow-wrap:break-word}.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-bg{color:var(--color-bg)}.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-muted{color:var(--color-muted)}.text-pr-merged{color:var(--color-pr-merged)}.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}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.underline{text-decoration-line:underline}.underline-offset-2{text-underline-offset:2px}.\[color-scheme\:dark\]{color-scheme:dark}.opacity-0{opacity:0}.opacity-40{opacity:.4}.opacity-60{opacity:.6}.opacity-100{opacity:1}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px 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)}.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-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px 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)}.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{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + 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-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)}.shadow-black\/40{--tw-shadow-color:#0006}@supports (color:color-mix(in lab,red,red)){.shadow-black\/40{--tw-shadow-color:color-mix(in oklab, color-mix(in oklab, var(--color-black) 40%, transparent) var(--tw-shadow-alpha), transparent)}}.shadow-black\/60{--tw-shadow-color:#0009}@supports (color:color-mix(in lab,red,red)){.shadow-black\/60{--tw-shadow-color:color-mix(in oklab, color-mix(in oklab, var(--color-black) 60%, transparent) var(--tw-shadow-alpha), transparent)}}.ring-surface{--tw-ring-color:var(--color-surface)}.blur{--tw-blur:blur(8px);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,)}.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-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;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\/steps\:invisible:is(:where(.group\/steps):is([open],:popover-open,:open) *){visibility:hidden}.group-open\/steps\:rotate-90:is(:where(.group\/steps):is([open],:popover-open,:open) *),.group-open\/think\:rotate-90:is(:where(.group\/think):is([open],:popover-open,:open) *){rotate:90deg}.peer-focus-visible\:outline-2:is(:where(.peer):focus-visible~*){outline-style:var(--tw-outline-style);outline-width:2px}.peer-focus-visible\:outline-offset-2:is(:where(.peer):focus-visible~*){outline-offset:2px}.peer-focus-visible\:outline-accent:is(:where(.peer):focus-visible~*){outline-color:var(--color-accent)}.placeholder\:font-sans::placeholder{font-family:var(--font-sans)}.placeholder\:text-faint::placeholder{color:var(--color-faint)}@media(hover:hover){.hover\:underline:hover{text-decoration-line:underline}}.focus\:border-accent\/50:focus{border-color:#8b7dff80}@supports (color:color-mix(in lab,red,red)){.focus\:border-accent\/50:focus{border-color:color-mix(in oklab,var(--color-accent) 50%,transparent)}}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\:outline-2:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px}.focus-visible\:-outline-offset-2:focus-visible{outline-offset:-2px}.focus-visible\:outline-accent:focus-visible{outline-color:var(--color-accent)}.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\:scale-\[0\.985\]:active{scale:.985}.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{background-color:var(--color-surface-2)}.active\:text-text:active{color:var(--color-text)}.active\:opacity-80:active{opacity:.8}.disabled\:bg-surface-2:disabled{background-color:var(--color-surface-2)}.disabled\:text-faint:disabled{color:var(--color-faint)}.disabled\:text-faint\/40:disabled{color:#5c616d66}@supports (color:color-mix(in lab,red,red)){.disabled\:text-faint\/40:disabled{color:color-mix(in oklab,var(--color-faint) 40%,transparent)}}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-60:disabled{opacity:.6}.has-\[textarea\:focus\]\:border-accent\/60:has(:is(textarea:focus)){border-color:#8b7dff99}@supports (color:color-mix(in lab,red,red)){.has-\[textarea\:focus\]\:border-accent\/60:has(:is(textarea:focus)){border-color:color-mix(in oklab,var(--color-accent) 60%,transparent)}}@media(prefers-reduced-motion:reduce){.motion-reduce\:transition-none{transition-property:none}}@media(min-width:400px){.min-\[400px\]\:block{display:block}}@media(min-width:48rem){.md\:static{position:static}.md\:inset-0{inset:0}.md\:inset-6{inset:calc(var(--spacing) * 6)}.md\:inset-x-auto{inset-inline:auto}.md\:top-16{top:calc(var(--spacing) * 16)}.md\:left-1\/2{left:50%}.md\:z-auto{z-index:auto}.md\:m-auto{margin:auto}.md\:mb-6{margin-bottom:calc(var(--spacing) * 6)}.md\:block{display:block}.md\:hidden{display:none}.md\:h-fit{height:fit-content}.md\:max-h-\[70vh\]{max-height:70vh}.md\:w-72{width:calc(var(--spacing) * 72)}.md\:w-\[36rem\]{width:36rem}.md\:max-w-\[92vw\]{max-width:92vw}.md\:max-w-none{max-width:none}.md\:shrink-0{flex-shrink:0}.md\:-translate-x-1\/2{--tw-translate-x: -50% ;translate:var(--tw-translate-x) var(--tw-translate-y)}.md\:translate-x-0{--tw-translate-x:0;translate:var(--tw-translate-x) var(--tw-translate-y)}.md\:rounded-2xl{border-radius:var(--radius-2xl)}.md\:rounded-3xl{border-radius:var(--radius-3xl)}.md\:border{border-style:var(--tw-border-style);border-width:1px}.md\:border-border{border-color:var(--color-border)}.md\:border-border-soft{border-color:var(--color-border-soft)}.md\:shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.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 typing{0%,60%,to{opacity:.35;transform:none}30%{opacity:1;transform:translateY(-3px)}}@keyframes fade-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}.fade-in{animation:.18s ease-out fade-in}@media(prefers-reduced-motion:reduce){.fade-in{opacity:1;animation:none}.typing-dot{opacity:.6;animation:none}}.pt-safe{padding-top:max(env(safe-area-inset-top),.5rem)}.pb-safe{padding-bottom:max(env(safe-area-inset-bottom),.5rem)}html[data-keyboard] .pb-safe{padding-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-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@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-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{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-outline-style{syntax:"*";inherits:false;initial-value:solid}@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
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
<title>Conductor Remote</title>
|
|
14
14
|
<!-- Runs before the module bundle so it can catch a stale shell that fails to boot. -->
|
|
15
15
|
<script src="/self-heal.js"></script>
|
|
16
|
-
<script type="module" crossorigin src="/assets/index-
|
|
17
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
16
|
+
<script type="module" crossorigin src="/assets/index-DjSimsDa.js"></script>
|
|
17
|
+
<link rel="stylesheet" crossorigin href="/assets/index-TXYaWW0Q.css">
|
|
18
18
|
<link rel="manifest" href="/manifest.webmanifest"></head>
|
|
19
19
|
<body>
|
|
20
20
|
<div id="root"></div>
|
package/dist/sw.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
if(!self.define){let e,s={};const i=(i,n)=>(i=new URL(i+".js",n).href,s[i]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=s,document.head.appendChild(e)}else e=i,importScripts(i),s()}).then(()=>{let e=s[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e}));self.define=(n,r)=>{const o=e||("document"in self?document.currentScript.src:"")||location.href;if(s[o])return;let l={};const t=e=>i(e,o),c={module:{uri:o},exports:l,require:t};s[o]=Promise.all(n.map(e=>c[e]||t(e))).then(e=>(r(...e),l))}}define(["./workbox-9c191d2f"],function(e){"use strict";importScripts("/push-sw.js"),self.addEventListener("message",e=>{e.data&&"SKIP_WAITING"===e.data.type&&self.skipWaiting()}),e.clientsClaim(),e.precacheAndRoute([{url:"self-heal.js",revision:"49bd63adb25a09341f8d2610e8bd3c76"},{url:"push-sw.js",revision:"e1e682e2e5e88fa03b7808ae9db8e098"},{url:"index.html",revision:"
|
|
1
|
+
if(!self.define){let e,s={};const i=(i,n)=>(i=new URL(i+".js",n).href,s[i]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=s,document.head.appendChild(e)}else e=i,importScripts(i),s()}).then(()=>{let e=s[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e}));self.define=(n,r)=>{const o=e||("document"in self?document.currentScript.src:"")||location.href;if(s[o])return;let l={};const t=e=>i(e,o),c={module:{uri:o},exports:l,require:t};s[o]=Promise.all(n.map(e=>c[e]||t(e))).then(e=>(r(...e),l))}}define(["./workbox-9c191d2f"],function(e){"use strict";importScripts("/push-sw.js"),self.addEventListener("message",e=>{e.data&&"SKIP_WAITING"===e.data.type&&self.skipWaiting()}),e.clientsClaim(),e.precacheAndRoute([{url:"self-heal.js",revision:"49bd63adb25a09341f8d2610e8bd3c76"},{url:"push-sw.js",revision:"e1e682e2e5e88fa03b7808ae9db8e098"},{url:"index.html",revision:"88e988110793096ae3b972efb1610222"},{url:"assets/workbox-window.prod.es5-BBnX5xw4.js",revision:null},{url:"assets/index-TXYaWW0Q.css",revision:null},{url:"assets/index-DjSimsDa.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\//]}))});
|
|
@@ -89,12 +89,13 @@ export class FirstPromptQueue {
|
|
|
89
89
|
* prompt lands (`null`) or is given up on (the failed entry) — awaited by API
|
|
90
90
|
* callers that asked to block, ignored by the phone.
|
|
91
91
|
*/
|
|
92
|
-
enqueue(workspaceId, text, sendImmediately = true, attachmentIds = []) {
|
|
92
|
+
enqueue(workspaceId, text, sendImmediately = true, attachmentIds = [], model) {
|
|
93
93
|
this.entries = [
|
|
94
94
|
...this.entries.filter(e => e.workspaceId !== workspaceId),
|
|
95
95
|
{
|
|
96
96
|
workspaceId,
|
|
97
97
|
text,
|
|
98
|
+
...(model?.trim() ? { model: model.trim() } : {}),
|
|
98
99
|
...(attachmentIds.length ? { attachmentIds } : {}),
|
|
99
100
|
status: 'waiting',
|
|
100
101
|
attempts: 0,
|
|
@@ -185,7 +186,8 @@ export class FirstPromptQueue {
|
|
|
185
186
|
if (!target?.sessionId)
|
|
186
187
|
return;
|
|
187
188
|
// It already went — the user sent it from the Mac, where the deep link left it
|
|
188
|
-
// pre-filled in the composer. Sending again would double it
|
|
189
|
+
// pre-filled in the composer. Sending again would double it, and changing the
|
|
190
|
+
// model now would affect a later turn instead of the first one they chose it for.
|
|
189
191
|
if (target.alreadySent)
|
|
190
192
|
return this.delivered(entry);
|
|
191
193
|
// Before `ready`, a send is worth trying and not worth judging: Conductor may not
|
|
@@ -209,7 +211,11 @@ export class FirstPromptQueue {
|
|
|
209
211
|
else
|
|
210
212
|
entry.attempts += 1;
|
|
211
213
|
this.save();
|
|
212
|
-
const result =
|
|
214
|
+
const result = entry.model
|
|
215
|
+
? await this.setModel(entry, target.sessionId)
|
|
216
|
+
: entry.text.trim()
|
|
217
|
+
? await this.deps.send(entry.workspaceId, target.sessionId, entry.text)
|
|
218
|
+
: { ok: true };
|
|
213
219
|
if (result.ok)
|
|
214
220
|
return this.delivered(entry);
|
|
215
221
|
if (result.blocked) {
|
|
@@ -235,6 +241,23 @@ export class FirstPromptQueue {
|
|
|
235
241
|
return this.fail(entry, error);
|
|
236
242
|
this.save();
|
|
237
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* Keep a model choice in the same attempt as its prompt. A successful picker
|
|
246
|
+
* change is persisted before the send starts, so a send retry cannot toggle a
|
|
247
|
+
* model back and forth. A workspace with no prompt settles after this step.
|
|
248
|
+
*/
|
|
249
|
+
async setModel(entry, sessionId) {
|
|
250
|
+
if (!entry.model)
|
|
251
|
+
return { ok: true };
|
|
252
|
+
if (!this.deps.setModel)
|
|
253
|
+
return { ok: false, error: 'the relay cannot set a model for a new workspace' };
|
|
254
|
+
const result = await this.deps.setModel(entry.workspaceId, sessionId, entry.model);
|
|
255
|
+
if (!result.ok)
|
|
256
|
+
return result;
|
|
257
|
+
entry.model = undefined;
|
|
258
|
+
this.save();
|
|
259
|
+
return entry.text.trim() ? this.deps.send(entry.workspaceId, sessionId, entry.text) : { ok: true };
|
|
260
|
+
}
|
|
238
261
|
/** Delivered: the entry's job is done, so it stops existing. */
|
|
239
262
|
delivered(entry) {
|
|
240
263
|
this.entries = this.entries.filter(e => e !== entry);
|
|
@@ -30,7 +30,7 @@ import { HIT_CLOSE, HIT_OPEN, workspaceTitle } from "./shared.js";
|
|
|
30
30
|
/** Versions we know how to speak. The client's choice wins when we know it. */
|
|
31
31
|
export const PROTOCOL_VERSIONS = ['2025-06-18', '2025-03-26', '2024-11-05'];
|
|
32
32
|
export const SERVER_INFO = { name: 'conductor-remote', version: '1' };
|
|
33
|
-
export const INSTRUCTIONS = 'Drives local Conductor agents through the conductor-remote relay. search_chats and read_chat reach archived workspaces, which is where most finished work lives. create_workspace
|
|
33
|
+
export const INSTRUCTIONS = 'Drives local Conductor agents through the conductor-remote relay. search_chats and read_chat reach archived workspaces, which is where most finished work lives. create_workspace opens its workspace link without direct UI control; a requested model and the first prompt are applied later through Conductor’s UI. dismiss_prompt, keep_awake and relay_logs touch no UI. send_prompt, split_chat, stop_turn, set_agent_options, a live list_models call and set_workspace_status drive the real Mac UI and steal focus for a few seconds — confirm with the user before using them on a chat they did not name.';
|
|
34
34
|
/** Reads are quick; a UI write is measured in tens of seconds (writes.ts ▸ SEND_ATTEMPT_MS). */
|
|
35
35
|
export const READ_TIMEOUT_MS = 10_000;
|
|
36
36
|
export const WRITE_TIMEOUT_MS = 75_000;
|
|
@@ -246,12 +246,16 @@ export function createTools(call) {
|
|
|
246
246
|
},
|
|
247
247
|
{
|
|
248
248
|
name: 'create_workspace',
|
|
249
|
-
description: 'Start a new Conductor workspace in a repo, optionally with a first prompt
|
|
249
|
+
description: 'Start a new Conductor workspace in a repo, optionally with a first prompt and model. The workspace starts from a Conductor deep link, so creation itself needs no Accessibility. The relay applies a requested model through Conductor’s UI after it creates the first chat and before it delivers the prompt. Returns as soon as the workspace row exists (~2s), before the worktree is built.',
|
|
250
250
|
inputSchema: {
|
|
251
251
|
type: 'object',
|
|
252
252
|
properties: {
|
|
253
253
|
repo: { type: 'string', description: 'Exact name from list_repos.' },
|
|
254
254
|
prompt: { type: 'string', description: 'First prompt for the new agent. Omit to open an empty workspace.' },
|
|
255
|
+
model: {
|
|
256
|
+
type: 'string',
|
|
257
|
+
description: 'Picker label from list_models with no session_id. The relay applies it before the first prompt, or configures an empty workspace once its chat exists.'
|
|
258
|
+
},
|
|
255
259
|
wait_for_send: {
|
|
256
260
|
type: 'boolean',
|
|
257
261
|
description: 'Block until the first prompt is actually delivered (tens of seconds, and longer behind other queued sends). Default false.'
|
|
@@ -266,12 +270,13 @@ export function createTools(call) {
|
|
|
266
270
|
run: async (args) => {
|
|
267
271
|
const repo = need(args, 'repo');
|
|
268
272
|
const prompt = str(args.prompt);
|
|
273
|
+
const model = str(args.model);
|
|
269
274
|
const send = args.wait_for_send === true;
|
|
270
275
|
// Default on, matching the relay: an omitted flag must never mean "wait".
|
|
271
276
|
const sendImmediately = args.send_immediately !== false;
|
|
272
277
|
const data = await call(routes.createWorkspace.path(), {
|
|
273
278
|
method: routes.createWorkspace.method,
|
|
274
|
-
body: { repo, prompt, send, sendImmediately },
|
|
279
|
+
body: { repo, prompt, model, send, sendImmediately },
|
|
275
280
|
timeoutMs: send ? WRITE_TIMEOUT_MS : 30_000
|
|
276
281
|
});
|
|
277
282
|
const lines = [
|
|
@@ -284,6 +289,8 @@ export function createTools(call) {
|
|
|
284
289
|
lines.push('the first prompt is queued — the relay delivers it, so do not send it again');
|
|
285
290
|
else if (data.sent)
|
|
286
291
|
lines.push('the first prompt was delivered');
|
|
292
|
+
if (data.model)
|
|
293
|
+
lines.push(data.configured ? `model applied: ${data.model}` : `model queued: ${data.model}`);
|
|
287
294
|
return lines.join('\n');
|
|
288
295
|
}
|
|
289
296
|
},
|
|
@@ -414,17 +421,30 @@ export function createTools(call) {
|
|
|
414
421
|
},
|
|
415
422
|
{
|
|
416
423
|
name: 'list_models',
|
|
417
|
-
description: 'The
|
|
424
|
+
description: 'The model labels the relay knows. With no session_id this returns its stored picker labels, grouped by harness; use it before create_workspace. With a session_id it reads that chat’s live Conductor picker and refreshes the stored labels. A live read DRIVES THE REAL UI — it focuses the workspace and opens the menu — so it costs a few seconds of stolen focus.',
|
|
418
425
|
inputSchema: {
|
|
419
426
|
type: 'object',
|
|
420
427
|
properties: {
|
|
421
|
-
session_id: {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
428
|
+
session_id: {
|
|
429
|
+
type: 'string',
|
|
430
|
+
description: 'The chat whose live picker to read. Omit to use the relay cache.'
|
|
431
|
+
},
|
|
432
|
+
workspace_id: {
|
|
433
|
+
type: 'string',
|
|
434
|
+
description: 'Workspace holding session_id. Required when session_id is set.'
|
|
435
|
+
}
|
|
436
|
+
}
|
|
425
437
|
},
|
|
426
438
|
run: async (args) => {
|
|
427
|
-
const sessionId =
|
|
439
|
+
const sessionId = str(args.session_id);
|
|
440
|
+
if (!sessionId) {
|
|
441
|
+
const data = await call(routes.modelCatalog.path());
|
|
442
|
+
if (!data.groups.length)
|
|
443
|
+
return 'no models are cached yet — read a chat’s live picker with session_id and workspace_id';
|
|
444
|
+
return data.groups
|
|
445
|
+
.map(group => `## ${group.agentType}\n${group.models.map(model => `- ${model}`).join('\n')}`)
|
|
446
|
+
.join('\n\n');
|
|
447
|
+
}
|
|
428
448
|
const workspaceId = need(args, 'workspace_id');
|
|
429
449
|
const data = await call(`${routes.models.path(sessionId)}?workspaceId=${encodeURIComponent(workspaceId)}`, { timeoutMs: WRITE_TIMEOUT_MS });
|
|
430
450
|
if (!data.ok)
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model-picker labels the relay has seen from Conductor.
|
|
3
|
+
*
|
|
4
|
+
* `sessions.model` is the model identifier Conductor saved for a chat. It is
|
|
5
|
+
* useful to confirm a change, but it is not reliably the label that Conductor's
|
|
6
|
+
* picker accepts. Keep the picker labels here instead, keyed by harness, so a
|
|
7
|
+
* new workspace can offer choices before it has a chat of its own.
|
|
8
|
+
*/
|
|
9
|
+
import fs from 'node:fs';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
import { modelPickerLabel } from "./shared.js";
|
|
12
|
+
function clean(models) {
|
|
13
|
+
return [
|
|
14
|
+
...new Set(models
|
|
15
|
+
.map(modelPickerLabel)
|
|
16
|
+
.map(model => model.trim())
|
|
17
|
+
.filter(Boolean))
|
|
18
|
+
].sort((a, b) => a.localeCompare(b));
|
|
19
|
+
}
|
|
20
|
+
function group(agentType) {
|
|
21
|
+
return agentType.trim() || 'unknown';
|
|
22
|
+
}
|
|
23
|
+
/** A small persisted cache. It contains labels Conductor has offered, never a hard-coded catalog. */
|
|
24
|
+
export class ModelCache {
|
|
25
|
+
file;
|
|
26
|
+
entries;
|
|
27
|
+
constructor(file) {
|
|
28
|
+
this.file = file;
|
|
29
|
+
this.entries = this.load();
|
|
30
|
+
}
|
|
31
|
+
list() {
|
|
32
|
+
return this.entries.map(entry => ({ ...entry, models: [...entry.models] }));
|
|
33
|
+
}
|
|
34
|
+
/** Record the menu that was read from one chat. An empty or failed read changes nothing. */
|
|
35
|
+
remember(agentType, models) {
|
|
36
|
+
const next = clean(models);
|
|
37
|
+
if (!next.length)
|
|
38
|
+
return;
|
|
39
|
+
const key = group(agentType ?? '');
|
|
40
|
+
const entry = {
|
|
41
|
+
agentType: key,
|
|
42
|
+
// A live menu is authoritative. Replacing its group drops a model Conductor
|
|
43
|
+
// stopped offering instead of leaving a stale choice in a later workspace.
|
|
44
|
+
models: next,
|
|
45
|
+
updatedAt: Date.now()
|
|
46
|
+
};
|
|
47
|
+
this.entries = [...this.entries.filter(existing => existing.agentType !== key), entry].sort((a, b) => a.agentType.localeCompare(b.agentType));
|
|
48
|
+
this.save();
|
|
49
|
+
}
|
|
50
|
+
/** A model that Conductor accepted is also a useful choice for a later workspace. */
|
|
51
|
+
rememberModel(agentType, model) {
|
|
52
|
+
if (!model)
|
|
53
|
+
return;
|
|
54
|
+
const key = group(agentType ?? '');
|
|
55
|
+
const current = this.entries.find(entry => entry.agentType === key);
|
|
56
|
+
const entry = {
|
|
57
|
+
agentType: key,
|
|
58
|
+
models: clean([...(current?.models ?? []), model]),
|
|
59
|
+
updatedAt: Date.now()
|
|
60
|
+
};
|
|
61
|
+
this.entries = [...this.entries.filter(existing => existing.agentType !== key), entry].sort((a, b) => a.agentType.localeCompare(b.agentType));
|
|
62
|
+
this.save();
|
|
63
|
+
}
|
|
64
|
+
load() {
|
|
65
|
+
try {
|
|
66
|
+
const parsed = JSON.parse(fs.readFileSync(this.file, 'utf8'));
|
|
67
|
+
if (!Array.isArray(parsed))
|
|
68
|
+
return [];
|
|
69
|
+
return parsed
|
|
70
|
+
.filter(entry => typeof entry?.agentType === 'string' && Array.isArray(entry.models))
|
|
71
|
+
.map(entry => ({
|
|
72
|
+
agentType: group(entry.agentType),
|
|
73
|
+
models: clean(entry.models.filter((model) => typeof model === 'string')),
|
|
74
|
+
updatedAt: Number.isFinite(entry.updatedAt) ? entry.updatedAt : 0
|
|
75
|
+
}))
|
|
76
|
+
.filter(entry => entry.models.length);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
save() {
|
|
83
|
+
try {
|
|
84
|
+
fs.mkdirSync(path.dirname(this.file), { recursive: true });
|
|
85
|
+
fs.writeFileSync(this.file, JSON.stringify(this.entries, null, 2), { mode: 0o600 });
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
console.warn(`[relay] could not persist model cache: ${err instanceof Error ? err.message : err}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
package/dist-node/src/routes.js
CHANGED
|
@@ -44,6 +44,8 @@ export const routes = {
|
|
|
44
44
|
state: flat('GET', '/api/state'),
|
|
45
45
|
search: flat('GET', '/api/search'),
|
|
46
46
|
repos: flat('GET', '/api/repos'),
|
|
47
|
+
/** Picker labels the relay has previously read from Conductor, grouped by harness. */
|
|
48
|
+
modelCatalog: flat('GET', '/api/models'),
|
|
47
49
|
repoIcon: param('GET', '/api/repos/:repo/icon'),
|
|
48
50
|
/** A temporary image emitted in a chat message, fetched with the phone's auth header. */
|
|
49
51
|
localImage: param('GET', '/api/local-images/:path'),
|
package/dist-node/src/server.js
CHANGED
|
@@ -15,6 +15,7 @@ import { workspaceDiff } from "./git.js";
|
|
|
15
15
|
import { installLogCapture, isManaged, LOG_FILE_NAMES, logFiles, processStartedAt, recentLogs, redactSecrets, tailLogFile } from "./logbuf.js";
|
|
16
16
|
import { createTools, handleRpc, READ_TIMEOUT_MS } from "./mcp-tools.js";
|
|
17
17
|
import { mergePr } from "./merge.js";
|
|
18
|
+
import { ModelCache } from "./model-cache.js";
|
|
18
19
|
import { armNoSleep, disarmNoSleep, MAX_SECONDS as NOSLEEP_MAX_SECONDS, nosleepState, watchNoSleepExpiry } from "./nosleep.js";
|
|
19
20
|
import { chatRoute, notifyAll, notifyDevice, pushConfig, startNotifier, subscribeDevice, unsubscribeDevice } from "./notify.js";
|
|
20
21
|
import { ParkedPromptQueue } from "./parked.js";
|
|
@@ -37,6 +38,10 @@ const db = new ConductorDb(cfg.dbPath);
|
|
|
37
38
|
const reads = new Reads(db, cfg.workspacesRoot);
|
|
38
39
|
const actuator = pickActuator(cfg.writeStrategy);
|
|
39
40
|
const STAGED_ATTACHMENTS_DIR = path.join(stateDir(), 'attachment-staging');
|
|
41
|
+
// Picker labels cannot be reconstructed from `sessions.model`, so they belong to
|
|
42
|
+
// relay state alongside the prompt queues. This lets a brand-new workspace choose
|
|
43
|
+
// from a list before Conductor has created its first chat.
|
|
44
|
+
const modelCache = new ModelCache(path.join(stateDir(), 'model-cache.json'));
|
|
40
45
|
// Full-text index over the chat prose, in the relay's own sidecar DB — never in
|
|
41
46
|
// Conductor's (see src/search.ts). It backfills in the background and is disposable:
|
|
42
47
|
// deleting the file rebuilds it on the next start.
|
|
@@ -307,6 +312,13 @@ const firstPrompts = new FirstPromptQueue(path.join(stateDir(), 'first-prompts.j
|
|
|
307
312
|
const result = await deliverPrompt(ws, sessionId, text);
|
|
308
313
|
return { ok: result.ok, error: result.error, blocked: lockBlocked(result.error) };
|
|
309
314
|
}),
|
|
315
|
+
setModel: (workspaceId, sessionId, model) => withUiPriority('background', async () => {
|
|
316
|
+
const ws = reads.getWorkspace(workspaceId);
|
|
317
|
+
if (!ws)
|
|
318
|
+
return { ok: false, error: 'the workspace is gone' };
|
|
319
|
+
const result = await applyAgentPatch(ws, sessionId, { model });
|
|
320
|
+
return { ok: result.ok, error: result.error, blocked: lockBlocked(result.error) };
|
|
321
|
+
}),
|
|
310
322
|
materialize: async (_workspaceId, worktree, attachmentIds) => {
|
|
311
323
|
try {
|
|
312
324
|
materializeStagedAttachments(STAGED_ATTACHMENTS_DIR, worktree, attachmentIds);
|
|
@@ -345,6 +357,10 @@ async function applyAgentPatch(ws, sessionId, patch) {
|
|
|
345
357
|
if (!(await confirmAgentOptions(ws, sessionId, opts))) {
|
|
346
358
|
return { ok: false, error: 'Conductor didn’t record the change — it may have been asleep. Try again.' };
|
|
347
359
|
}
|
|
360
|
+
if (patch.model) {
|
|
361
|
+
const session = reads.listSessions(ws.id).find(row => row.id === sessionId);
|
|
362
|
+
modelCache.rememberModel(session?.agent_type, patch.model);
|
|
363
|
+
}
|
|
348
364
|
return { ok: true };
|
|
349
365
|
}
|
|
350
366
|
/**
|
|
@@ -777,6 +793,11 @@ const server = http.createServer(async (req, res) => {
|
|
|
777
793
|
if (isRoute(routes.repos, req.method, pathname)) {
|
|
778
794
|
return json(req, res, 200, { repos: reads.listRepos() });
|
|
779
795
|
}
|
|
796
|
+
// GET /api/models — prior live picker reads, without activating Conductor. A
|
|
797
|
+
// new workspace has no chat yet, so this is its only safe source of choices.
|
|
798
|
+
if (isRoute(routes.modelCatalog, req.method, pathname)) {
|
|
799
|
+
return json(req, res, 200, { groups: modelCache.list() });
|
|
800
|
+
}
|
|
780
801
|
// GET /api/settings — relay preferences plus what the phone needs to edit them:
|
|
781
802
|
// the SSIDs this Mac already holds credentials for, so the picker offers a choice
|
|
782
803
|
// instead of asking someone to type a network name from memory on a phone keyboard.
|
|
@@ -930,6 +951,9 @@ const server = http.createServer(async (req, res) => {
|
|
|
930
951
|
if (isRoute(routes.createWorkspace, req.method, pathname)) {
|
|
931
952
|
const body = JSON.parse((await readBody(req)) || '{}');
|
|
932
953
|
const attachmentIds = body.attachmentIds ?? [];
|
|
954
|
+
if (body.model !== undefined && typeof body.model !== 'string')
|
|
955
|
+
return json(req, res, 400, { error: 'model must be a picker label' });
|
|
956
|
+
const model = body.model?.trim() || undefined;
|
|
933
957
|
if (!Array.isArray(attachmentIds) || attachmentIds.some(id => typeof id !== 'string'))
|
|
934
958
|
return json(req, res, 400, { error: 'attachment ids must be a list of strings' });
|
|
935
959
|
const attachments = stagedAttachments(STAGED_ATTACHMENTS_DIR, attachmentIds);
|
|
@@ -973,8 +997,8 @@ const server = http.createServer(async (req, res) => {
|
|
|
973
997
|
// its own schedule and the phone watches it in /api/state; `send:true` opts API
|
|
974
998
|
// callers into waiting.
|
|
975
999
|
// Whatever happens, the prompt is already pre-filled in Conductor's composer.
|
|
976
|
-
const settled = prompt
|
|
977
|
-
? firstPrompts.enqueue(created.id, prompt, body.sendImmediately !== false, attachmentIds)
|
|
1000
|
+
const settled = prompt || model
|
|
1001
|
+
? firstPrompts.enqueue(created.id, prompt, body.sendImmediately !== false, attachmentIds, model)
|
|
978
1002
|
: null;
|
|
979
1003
|
const failed = settled && body.send === true ? await settled : null;
|
|
980
1004
|
settled?.catch(() => undefined); // fire-and-forget: it reports failure, it never rejects
|
|
@@ -983,8 +1007,11 @@ const server = http.createServer(async (req, res) => {
|
|
|
983
1007
|
workspaceId: created.id,
|
|
984
1008
|
workspace: reads.getWorkspace(created.id) ?? created,
|
|
985
1009
|
pendingPrompt: prompt || undefined,
|
|
986
|
-
|
|
987
|
-
|
|
1010
|
+
model,
|
|
1011
|
+
sent: body.send === true && !!prompt && !failed,
|
|
1012
|
+
configured: body.send === true && !!model && !failed,
|
|
1013
|
+
warning: failed?.error &&
|
|
1014
|
+
`Workspace created; the initial ${model ? 'model selection and prompt' : 'prompt'} didn’t finish (${failed.error}).`
|
|
988
1015
|
});
|
|
989
1016
|
}
|
|
990
1017
|
// GET /api/repos/:name/icon — the repo's resolved sidebar icon (see src/icons.ts)
|
|
@@ -1113,6 +1140,8 @@ const server = http.createServer(async (req, res) => {
|
|
|
1113
1140
|
if ('error' in located)
|
|
1114
1141
|
return json(req, res, 409, { error: located.error });
|
|
1115
1142
|
const result = await listAgentModels({ workspace: ws, sessionId, tab: located.tab });
|
|
1143
|
+
if (result.ok && result.models)
|
|
1144
|
+
modelCache.remember(located.session?.agent_type, result.models);
|
|
1116
1145
|
return json(req, res, result.ok ? 200 : 502, result);
|
|
1117
1146
|
}
|
|
1118
1147
|
// POST /api/sessions/:id/agent { effort?, plan?, fast?, model? }
|
package/dist-node/src/shared.js
CHANGED
|
@@ -58,6 +58,50 @@ export function queryTokens(raw) {
|
|
|
58
58
|
export function modelPickerLabel(label) {
|
|
59
59
|
return label.endsWith(' NEW') ? label.slice(0, -4) : label;
|
|
60
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Conductor's labels omit the agent for its built-in models. Keep them in the
|
|
63
|
+
* same provider sections as Conductor's picker: Claude Code, Codex, Cursor,
|
|
64
|
+
* and OpenCode. An unknown label belongs in Other until Conductor gives us a
|
|
65
|
+
* provider-qualified name or this small mapping gains its family.
|
|
66
|
+
*/
|
|
67
|
+
function modelProvider(model) {
|
|
68
|
+
const label = modelPickerLabel(model).trim().toLowerCase();
|
|
69
|
+
if (label.startsWith('anthropic/'))
|
|
70
|
+
return 'Anthropic';
|
|
71
|
+
if (label.startsWith('openai/'))
|
|
72
|
+
return 'OpenAI';
|
|
73
|
+
if (label.startsWith('cursor/'))
|
|
74
|
+
return 'Cursor';
|
|
75
|
+
if (/^(claude|fable|haiku|opus|sonnet)(?:[\s-]|$)/.test(label))
|
|
76
|
+
return 'Anthropic';
|
|
77
|
+
if (/^(?:auto|gpt|o[1-9]|\d)(?:[\s.-]|$)/.test(label))
|
|
78
|
+
return 'OpenAI';
|
|
79
|
+
if (/^(composer|grok)(?:[\s-]|$)/.test(label))
|
|
80
|
+
return 'Cursor';
|
|
81
|
+
if (/^opencode(?:-go)?\//.test(label))
|
|
82
|
+
return 'OpenCode';
|
|
83
|
+
const slash = label.indexOf('/');
|
|
84
|
+
if (slash > 0)
|
|
85
|
+
return label.slice(0, slash);
|
|
86
|
+
return 'Other';
|
|
87
|
+
}
|
|
88
|
+
/** Stable, case-insensitive grouping shared by every model selector. */
|
|
89
|
+
export function groupModelPickerLabels(models) {
|
|
90
|
+
const grouped = new Map();
|
|
91
|
+
for (const raw of models) {
|
|
92
|
+
const model = modelPickerLabel(raw).trim();
|
|
93
|
+
if (!model)
|
|
94
|
+
continue;
|
|
95
|
+
const provider = modelProvider(model);
|
|
96
|
+
const entries = grouped.get(provider) ?? [];
|
|
97
|
+
if (!entries.includes(model))
|
|
98
|
+
entries.push(model);
|
|
99
|
+
grouped.set(provider, entries);
|
|
100
|
+
}
|
|
101
|
+
return [...grouped]
|
|
102
|
+
.map(([label, entries]) => ({ label, models: entries.sort((a, b) => a.localeCompare(b)) }))
|
|
103
|
+
.sort((a, b) => a.label.localeCompare(b.label));
|
|
104
|
+
}
|
|
61
105
|
/**
|
|
62
106
|
* Markers the relay wraps search hits in (src/search.ts, via FTS5 `snippet()`). They
|
|
63
107
|
* are control characters, so they must never reach the DOM: an unsplit snippet renders
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "conductor-remote",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.54.1",
|
|
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.",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"typecheck": "tsc -p tsconfig.json",
|
|
51
51
|
"lint": "biome check .",
|
|
52
52
|
"fix": "biome check . --fix",
|
|
53
|
-
"verify": "yarn typecheck && yarn lint && yarn check:imports && yarn check:routes && yarn check:attachments && yarn check:applescript && yarn check:config && yarn check:utf8-target && yarn check:file-links && yarn check:model-labels && yarn check:nosleep && yarn check:uilock && yarn check:firstprompt && yarn check:sendonce && yarn check:notify && yarn check:online",
|
|
53
|
+
"verify": "yarn typecheck && yarn lint && yarn check:imports && yarn check:routes && yarn check:attachments && yarn check:applescript && yarn check:config && yarn check:utf8-target && yarn check:file-links && yarn check:model-cache && yarn check:model-labels && yarn check:nosleep && yarn check:uilock && yarn check:firstprompt && yarn check:sendonce && yarn check:notify && yarn check:online",
|
|
54
54
|
"release": "semantic-release",
|
|
55
55
|
"prepack": "yarn build && yarn build:node",
|
|
56
56
|
"postinstall": "husky || true",
|
|
@@ -60,6 +60,7 @@
|
|
|
60
60
|
"check:utf8-target": "node scripts/check-utf8-target.ts",
|
|
61
61
|
"check:file-links": "node scripts/check-file-links.ts",
|
|
62
62
|
"check:firstprompt": "node scripts/check-firstprompt.ts",
|
|
63
|
+
"check:model-cache": "node scripts/check-model-cache.ts",
|
|
63
64
|
"check:model-labels": "node scripts/check-model-labels.ts",
|
|
64
65
|
"check:sendonce": "node scripts/check-sendonce.ts",
|
|
65
66
|
"check:notify": "node scripts/check-notify.ts",
|