@coool/opencode-adaptive 1.2.3 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/gui-experimental.cjs +3 -1
- package/dist/gui.d.ts +2 -1
- package/dist/gui.js +7 -5
- package/dist/gui.js.map +1 -1
- package/dist/tui.js +5 -3
- package/dist/tui.js.map +1 -1
- package/dist/ui/{tui → flow}/controller.d.ts +5 -8
- package/dist/ui/{tui → flow}/controller.js +4 -9
- package/dist/ui/flow/controller.js.map +1 -0
- package/dist/ui/flow/menu.js.map +1 -0
- package/dist/ui/flow/options.d.ts +12 -0
- package/dist/ui/flow/options.js.map +1 -0
- package/dist/ui/{tui → flow}/receipts.d.ts +1 -1
- package/dist/ui/{tui → flow}/receipts.js +4 -7
- package/dist/ui/flow/receipts.js.map +1 -0
- package/dist/ui/{tui → flow}/status.d.ts +0 -2
- package/dist/ui/{tui → flow}/status.js +0 -6
- package/dist/ui/flow/status.js.map +1 -0
- package/dist/ui/{tui → flow}/tiers.d.ts +2 -9
- package/dist/ui/{tui → flow}/tiers.js +5 -25
- package/dist/ui/flow/tiers.js.map +1 -0
- package/dist/ui/gui/api.d.ts +6 -2
- package/dist/ui/gui/api.js +4 -4
- package/dist/ui/gui/api.js.map +1 -1
- package/dist/ui/gui/flow.d.ts +23 -0
- package/dist/ui/gui/flow.js +123 -0
- package/dist/ui/gui/flow.js.map +1 -0
- package/dist/ui/gui/view.js +2 -2
- package/dist/ui/gui/view.js.map +1 -1
- package/dist/ui/tui/search.d.ts +9 -0
- package/dist/ui/tui/search.js +10 -0
- package/dist/ui/tui/search.js.map +1 -0
- package/package.json +2 -1
- package/dist/ui/tui/controller.js.map +0 -1
- package/dist/ui/tui/menu.js.map +0 -1
- package/dist/ui/tui/options.d.ts +0 -19
- package/dist/ui/tui/options.js.map +0 -1
- package/dist/ui/tui/receipts.js.map +0 -1
- package/dist/ui/tui/status.js.map +0 -1
- package/dist/ui/tui/tiers.js.map +0 -1
- /package/dist/ui/{tui → flow}/menu.d.ts +0 -0
- /package/dist/ui/{tui → flow}/menu.js +0 -0
- /package/dist/ui/{tui → flow}/options.js +0 -0
|
@@ -1 +1,3 @@
|
|
|
1
|
-
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const a=require("solid-js/web"),O=require("@opencode/plugin/gui"),m=require("solid-js"),X=require("@opencode/schema/rpc"),Z=128,K="coool.adaptive",N="coool.adaptive",S={internal:M()};function l(e,t){return{"~standard":{version:1,vendor:N,validate:i=>e(i)?{value:i}:{issues:[{message:t}]}}}}function M(){return{"~standard":{version:1,vendor:N,validate:e=>e===void 0?{value:void 0}:{issues:[{message:"unexpected input: no input expected"}]}}}}const v=e=>typeof e=="object"&&e!==null&&!Array.isArray(e),T=e=>typeof e=="string",D=e=>{if(!(v(e)&&T(e.sessionID)))return!1;const t=e.sessionID;return t.length>0&&t.length<=Z},Q=e=>v(e)&&T(e.sessionID)&&(e.mode===null||T(e.mode)),ee=e=>v(e)&&T(e.sessionID)&&(e.tier===null||T(e.tier)),x=X.Rpc.define({id:K,methods:{"settings.get":{errors:S,input:M(),output:l(v,"output must be an object")},"settings.set":{errors:S,input:l(v,"input must be an object"),output:l(v,"output must be an object")},"status.get":{errors:S,input:M(),output:l(v,"output must be an object")},"receipts.list":{errors:S,input:M(),output:l(Array.isArray,"output must be an array")},"session-mode.get":{errors:S,input:l(D,"input must be an object with a sessionID string"),output:l(v,"output must be an object")},"session-mode.set":{errors:S,input:l(Q,"input must be an object with a sessionID string and a mode string or null"),output:l(v,"output must be an object")},"tier-request.get":{errors:S,input:l(D,"input must be an object with a sessionID string"),output:l(v,"output must be an object")},"tier-request.set":{errors:S,input:l(ee,"input must be an object with a sessionID string and a tier string or null"),output:l(v,"output must be an object")}},events:{"settings-changed":{schema:l(v,"event data must be an object")},"session-mode-changed":{schema:l(D,"event data must be an object with a sessionID string")},"tier-request-changed":{schema:l(D,"event data must be an object with a sessionID string")}}}),E=["off","observe","active"],w=["quick","standard","advanced"];function te(e,t){const i=e.rpc(x);return{status:()=>i["status.get"](void 0,t()).catch(()=>{}),receipts:()=>i["receipts.list"](void 0,t()).catch(()=>{}),update:async n=>{try{return(await i["settings.set"](n,t())).ok}catch{return!1}},watchSettings:n=>i.events.on("settings-changed",()=>{n()})}}function f(e){const t=`${e.providerID}/${e.id}`;return e.variant===void 0?t:`${t}@${e.variant}`}function L(e){if(e===void 0)return"";const t=[];return e.health!=="healthy"&&t.push(e.health),e.breaker==="open"?t.push("breaker-open"):e.breaker==="half_open"&&t.push("half-open"),e.windowed&&t.push("windowed"),t.length===0?"":` [${t.join(", ")}]`}function ie(e,t,i){const n=[{title:"Add model…",value:{kind:"add"},description:"Search OpenCode models"}],o=i==null?void 0:i.tiers[t];return e.tiers[t].forEach((r,s)=>{n.push({title:`${f(r)}${L(o==null?void 0:o[s])}`,value:{kind:"resource",index:s,ref:r},description:"Move or remove"})}),n}function ne(e){return w.map(t=>{const i=e.tiers[t],n=i.length===0?"(empty — add models to enable routing)":i.map(o=>f(o.ref)).join(" · ");return{title:`${t} (${String(i.length)})`,value:t,description:n}})}function oe(e){return e.map(t=>({title:`${t.providerID}/${t.modelID}`,description:t.name,value:t}))}function re(e){const t=[{title:"(model default)",value:{variant:void 0}}];for(const i of e)t.push({title:`@${i}`,value:{variant:i}});return t}function se(e,t){return[...e.filter((i,n)=>n===t),...e.filter((i,n)=>n!==t)]}function ae(e,t,i){const n=[...e],o=t+i,r=n[t],s=n[o];return r===void 0||s===void 0||(n[t]=s,n[o]=r),n}function ce(e,t){return e.filter((i,n)=>n!==t)}function ue(e){const t=[];for(const i of w)for(const n of e.tiers[i])t.push({tier:i,status:n});return t}function j(e){const t=new Date(e),i=String(t.getHours()).padStart(2,"0"),n=String(t.getMinutes()).padStart(2,"0"),o=String(t.getMonth()+1).padStart(2,"0"),r=String(t.getDate()).padStart(2,"0");return`${String(t.getFullYear())}-${o}-${r} ${i}:${n}`}function de(e,t){return t===null?!1:t.providerID===e.providerID&&t.id===e.id&&t.variant===e.variant}function le(e,t){let i="";return e.windowed&&(i+=e.windowUntil===null?", windowed (deadline unknown)":`, windowed until ${j(e.windowUntil)}`),e.usedPercent!==null&&(i+=`, used ${String(e.usedPercent)}%`,e.resetAt!==null&&(i+=`, resets ${j(e.resetAt)}`)),e.breaker==="open"?i+=", breaker open":e.breaker==="half_open"&&(i+=", probing"),de(e.ref,t)&&(i+=" (pinned)"),i}function pe(e,t){return le(e,t).replace(/^[, ]+/,"")||"-"}function ve(e){const t=["MODEL","TIER","HEALTH","STATE"],i=ue(e).map(({tier:r,status:s})=>[f(s.ref),r,s.health,pe(s,e.pin)]),n=t.map((r,s)=>Math.max(r.length,...i.map(c=>c[s].length))),o=r=>r.map((s,c)=>s.padEnd(n[c])).join(" ").trimEnd();return[o(t),...i.map(o)]}const fe="No models configured yet — open Models to add some.",ge=["status","models","mode","tier","pin","receipts"];function he(e){if(e===void 0)return"how every configured model is doing";const t=w.reduce((o,r)=>o+e.tiers[r].length,0);if(t===0)return"no models configured yet";const i=w.reduce((o,r)=>o+e.tiers[r].filter(s=>s.windowed).length,0),n=i===0?"":` · ${String(i)} windowed`;return`${String(t)} resource${t===1?"":"s"}${n}`}function me(e){return w.map(i=>`${i}: ${String(e.tiers[i].length)}`).join(" · ")}function we(e){return e===void 0?"pin a tier for the current session":e===null?"not pinned — the routing ladder decides":`pinned to ${e} — steers this session`}function be(e,t){if(e===t.override)return"override";if(e===t.effective)return"current"}function $e(e,t,i,n=e.mode){const o=r=>{switch(r){case"status":return{title:"Status",value:r,description:he(t)};case"models":return{title:"Models",value:r,description:me(e)};case"mode":return{title:"Mode",value:r,description:`currently ${n}`};case"tier":return{title:"Tier",value:r,description:we(i)};case"pin":return{title:"Pin",value:r,description:e.pin===null?"force one resource":`pinned to ${f(e.pin)}`};case"receipts":return{title:"Receipts",value:r,description:"why each turn ran the model it ran"}}};return ge.map(o)}function Se(e){return E.map(t=>({title:t===e?`${t} (current)`:t,value:t,description:V(t)}))}function V(e){switch(e){case"off":return"Adaptive does nothing";case"observe":return"Record decisions without applying them";case"active":return"Apply routing and recovery decisions"}}function ye(e,t){return[{title:"Global mode…",description:`applies to every session without an override (now ${e})`,value:"global"},{title:"This session…",description:`override for this session only (now ${t})`,value:"session"}]}function ke(e){const t=[{title:`Follow global (${e.global})`,description:"clear this session's override",value:{kind:"global"}}];for(const i of E){const n=be(i,e);t.push({title:n===void 0?i:`${i} (${n})`,value:{kind:"mode",mode:i},description:V(i)})}return t}function Ie(e){switch(e){case"quick":return"routine edits, mechanical steps";case"standard":return"the default";case"advanced":return"multi-step design, broad refactors"}}function Te(e){const t=[{title:"Follow routing ladder",description:"clear this session's tier request",value:{kind:"ladder"}}];for(const i of w)t.push({title:i===e.pinned?`${i} (pinned)`:i,value:{kind:"tier",tier:i},description:Ie(i)});return t}function F(e){if(e instanceof Error)return e.message;if(typeof e=="object"&&e!==null){const t=e.message;return typeof t=="string"&&t.length>0?t:JSON.stringify(e,(i,n)=>typeof n=="bigint"?String(n):n)}return String(e)}function C(e){switch(e.kind){case"apply":{const t=e.tier===void 0?"":` [${e.tier}]`;return`apply ${f(e.model)}${t} (${e.reason})`}case"defer":{const t=e.detail===void 0?"":` — ${e.detail}`;return`defer: ${e.reason}${t}`}case"park":{const t=e.detail===void 0?"":` — ${e.detail}`;return`park: ${e.reason}${t}`}case"escalate":return`escalate ${e.step} → ${f(e.target)} [${e.tier}]`;case"observe-only":return`observe: ${C(e.would)}`}}function G(e){return e===void 0?"pending":e.status==="succeeded"?"succeeded":e.status==="failed"?`failed (${e.category})`:`interrupted (${e.reason})`}function H(e){return`#${String(e.id)} ${C(e.decision)} — ${G(e.outcome)}`}function Ae(e,t=Date.now()){return[`id: ${String(e.id)}`,`when: ${De(e.at,t)} (${new Date(e.at).toISOString()})`,`session: ${e.sessionID}`,`unit: ${e.unitID??"-"}`,`tier: ${e.tier??"-"}`,`decision: ${C(e.decision)}`,`outcome: ${G(e.outcome)}`,"",`What happened: ${Me(e)}`].join(`
|
|
2
|
+
`)}function De(e,t){const i=Math.max(0,Math.floor((t-e)/1e3));if(i<60)return"just now";const n=Math.floor(i/60);if(n<60)return`${String(n)}m ago`;const o=Math.floor(n/60);return o<24?`${String(o)}h ago`:`${String(Math.floor(o/24))}d ago`}function Me(e){const t=e.decision;switch(t.kind){case"apply":return _e[t.reason];case"defer":return Ee[t.reason];case"park":return Re[t.reason];case"escalate":return`This turn needed stronger capability, so Adaptive moved it to ${f(t.target)}.`;case"observe-only":return"Adaptive is observing: this is what it would have done, without applying it."}}const _e={pin:"Adaptive routed to the resource you pinned.",preferred:"Adaptive chose this resource: it is the preferred resource for the tier and was available.",fallback:"The preferred resource was unavailable, so Adaptive moved the turn to the next resource in the tier.",probe:"Adaptive sent this turn as a recovery probe after an outage, to check whether the resource works again.",draining:"Adaptive routed to this resource while its provider was draining capacity."},Ee={"routing-defer":"No routing rule applied to this turn, so Adaptive left the choice to OpenCode.","adaptive-off":"Adaptive is switched off, so every turn runs on OpenCode's own choice.","pin-unverifiable":"The pinned resource could not be verified as available, so Adaptive left the choice to OpenCode.","pin-incompatible":"The pinned resource does not fit this turn's requirements, so Adaptive left the choice to OpenCode.","escalation-not-justified":"No structured capability evidence justified escalation, so Adaptive kept the current capability.","escalation-budget-exhausted":"This unit has used its escalation budget; no further escalation is allowed.","escalation-ceiling":"Adaptive is already at the highest capability tier; no stronger tier is configured.","escalation-no-candidate":"No stronger resource was eligible to escalate to (unavailable, blocked, or not configured)."},Re={"all-blocked":"Every resource in the tier is currently unavailable, so the turn ran without Adaptive routing.","no-candidates":"There are no models configured for this tier, so the turn ran without Adaptive routing.","no-compatible-resource":"No configured resource fits this turn's requirements, so the turn ran without Adaptive routing.","pin-blocked":"The pinned resource is unavailable and no fallback is allowed while pinned."},_=20;function Oe(e){return e.slice(0,_)}const P="OpenCode Adaptive",Ce="Adaptive starts in Observe mode: routing decisions are recorded as receipts, but no model is switched. Open /adaptive to configure tiers or switch to Active.";async function b(e,t){try{return await t()}catch(i){e.view.toast(F(i),"error");return}}async function y(e,t,i){const n=await b(e,()=>e.api.setSettings(t));if(n!==void 0){if(!n.ok){await e.view.alert({title:"Invalid settings",message:n.errors.join("; ")});return}return i.once||(e.view.toast("Settings saved","success"),i.once=!0),n.settings}}async function q(e,t,i,n){const o=await b(e,()=>e.api.setSessionMode(t,i));if(o!==void 0){if(!o.ok){await e.view.alert({title:"Invalid settings",message:o.errors.join("; ")});return}return n.once||(e.view.toast("Settings saved","success"),n.once=!0),o}}async function je(e,t,i,n){const o=await b(e,()=>e.api.setTierRequest(t,i));if(o!==void 0){if(!o.ok){await e.view.alert({title:"Invalid tier request",message:o.errors.join("; ")});return}return n.once||(e.view.toast("Settings saved","success"),n.once=!0),o}}async function Pe(e){const t=await b(e,()=>e.api.settings());if(t===void 0)return;let i=t;const n={once:!1},o=e.sessionID;let r=o===void 0?null:await b(e,()=>e.api.sessionMode(o))??null;if(!i.inviteDismissed){await e.view.confirm({title:P,message:Ce});const s=await e.view.select({title:P,options:[{title:"Open the menu now",value:!0},{title:"Later",value:!1}]}),c=await y(e,{inviteDismissed:!0},n);if(c!==void 0&&(i=c),s!==!0)return}for(;;){const[s,c]=await Promise.all([e.api.settings().catch(()=>{}),e.api.status().catch(()=>{})]);s!==void 0&&(i=s);const p=o===void 0?void 0:await e.api.tierRequest(o).catch(()=>{}),d=await e.view.select({title:`Adaptive (${r??i.mode})`,options:$e(i,c,p,r??i.mode)});if(d===void 0)return;if(d==="status"){await xe(e);continue}if(d==="models"){i=await Le(e,n)??i;continue}if(d==="mode"){const g=await qe(e,i,n,o,r);g!==void 0&&(i=g.settings,r=g.override);continue}if(d==="tier"){if(o===void 0){await e.view.alert({title:"This session",message:"This session's tier request can only be changed inside a session. Open a session and run /adaptive from there."});continue}await Ne(e,o,n);continue}if(d==="pin"){i=await Ue(e,i,n)??i;continue}await We(e)}}async function qe(e,t,i,n,o){for(;;){const r=await e.view.select({title:"Operating mode",options:ye(t.mode,o??t.mode)});if(r===void 0)return;if(r==="global"){const p=await e.view.select({title:"Global mode",options:Se(t.mode)});if(p===void 0||p===t.mode)return;const d=await y(e,{mode:p},i);return d===void 0?void 0:{settings:d,override:o}}if(n===void 0){await e.view.alert({title:"This session",message:"This session's mode can only be changed inside a session. Open a session and run /adaptive from there."});continue}const s=await e.view.select({title:"This session's mode",options:ke({global:t.mode,override:o,effective:o??t.mode})});return s===void 0?void 0:s.kind==="global"?o===null||await q(e,n,null,i)===void 0?void 0:{settings:t,override:null}:s.mode===(o??t.mode)||await q(e,n,s.mode,i)===void 0?void 0:{settings:t,override:s.mode}}}async function Ne(e,t,i){const n=await b(e,()=>e.api.tierRequest(t));if(n===void 0)return;const o=await e.view.select({title:"This session's tier",options:Te({pinned:n})});if(o===void 0)return;const r=o.kind==="ladder"?null:o.tier;r!==n&&await je(e,t,r,i)}async function xe(e){const t=await b(e,()=>e.api.status());if(t===void 0)return;if(!w.some(n=>t.tiers[n].length>0)){await e.view.alert({title:"Status",message:fe});return}await e.view.table({title:"Status",lines:ve(t)})}async function Le(e,t){let i;for(;;){const n=await b(e,()=>e.api.settings());if(n===void 0)return i;const o=await b(e,()=>e.api.status());if(o===void 0)return i;const r=await e.view.select({title:"Tier",options:ne(o)});if(r===void 0)return i;let s=o,c=n;for(;;){const p=await e.view.select({title:`Tier: ${r}`,options:ie(c,r,s)});if(p===void 0)break;if(p.kind==="add"){const u=await Ve(e,c,r,t);u!==void 0&&(i=u,c=u)}else{const u=await Ge(e,c,r,p,t);u!==void 0&&(i=u,c=u)}const[d,g]=await Promise.all([e.api.settings().catch(()=>{}),e.api.status().catch(()=>{})]);d!==void 0&&(i=d,c=d),g!==void 0&&(s=g)}}}async function Ve(e,t,i,n){const o=e.models(),r=await e.view.select({title:"Add model",placeholder:"Search provider / model / name",options:oe(o)});if(r===void 0)return;const s=r.variants;let c;if(s.length>0){const g=await e.view.select({title:`Variant: ${r.providerID}/${r.modelID}`,options:re(s)});if(g===void 0)return;c=g.variant}const p=c===void 0?{providerID:r.providerID,id:r.modelID}:{providerID:r.providerID,id:r.modelID,variant:c},d=[...t.tiers[i],p];return y(e,{tiers:{...t.tiers,[i]:d}},n)}function Fe(){return[{title:"Move to top",value:"top",description:"Make this the primary resource"},{title:"Move up",value:"up",description:"Higher priority within the tier"},{title:"Move down",value:"down",description:"Lower priority within the tier"},{title:"Remove",value:"remove"}]}async function Ge(e,t,i,{index:n,ref:o},r){const s=await e.view.select({title:`Resource: ${f(o)}`,options:Fe()});if(s===void 0)return;if(s==="remove")return await e.view.confirm({title:"Remove resource",message:`Remove ${f(o)} from ${i}?`})!==!0?void 0:y(e,{tiers:{...t.tiers,[i]:ce(t.tiers[i],n)}},r);const c=s==="top"?se(t.tiers[i],n):ae(t.tiers[i],n,s==="up"?-1:1);return y(e,{tiers:{...t.tiers,[i]:c}},r)}function He(e){const t=[{title:"No pin",value:{kind:"clear"},description:"clear the pin"}];for(const i of w)for(const n of e.tiers[i])t.push({title:`${f(n)} (${i})`,description:"route every turn to this resource, even in active mode",value:{kind:"pin",ref:n}});return t}async function Ue(e,t,i){if(!w.some(r=>t.tiers[r].length>0)){await e.view.alert({title:"Pin",message:"(no configured resources to pin)"});return}const o=await e.view.select({title:"Pin",options:He(t)});if(o!==void 0)return y(e,{pin:o.kind==="clear"?null:o.ref},i)}async function We(e){const t=await b(e,()=>e.api.receipts());if(t!==void 0){if(t.length===0){await e.view.alert({title:"Receipts",message:"No decisions recorded yet."});return}for(let i=0;;){const n=Oe(t.slice(i)),o=i+_<t.length,r=await e.view.selectPaged({title:`Receipts (${String(t.length)}, newest first)`,options:n.map(c=>({title:H(c),value:c})),actions:[...i>0?[{title:"Previous page",value:"prev",bind:"ctrl+b"}]:[],...o?[{title:"Next page",value:"next",bind:"ctrl+f"}]:[]]});if(r.action==="next"){i+=_;continue}if(r.action==="prev"){i=Math.max(0,i-_);continue}const s=r.picked;if(s===void 0)return;await e.view.alert({title:`Receipt #${String(s.id)}`,message:Ae(s)})}}}var ze=a.template("<pre class=adaptive-table>");function Je(e){return m.onCleanup(e.done),a.createComponent(e.host.ui.Dialog,{get title(){return e.title},size:"x-large",get footer(){return a.createComponent(e.host.ui.Button,{get onClick(){return e.close},children:"Close"})},get children(){var t=ze();return a.insert(t,()=>e.lines.join(`
|
|
3
|
+
`)),t}})}function Ye(e){return{select:({title:t,placeholder:i,options:n})=>e.dialogs.select({title:t,options:n,...i!==void 0?{placeholder:i}:{}}),selectPaged:async t=>{const i=await e.dialogs.select({title:t.title,options:[...t.options.map(n=>({title:n.title,value:{kind:"pick",value:n.value}})),...t.actions.map(n=>({title:n.title,value:{kind:"action",value:n.value}}))]});return i===void 0?{picked:void 0,action:void 0}:i.kind==="pick"?{picked:i.value,action:void 0}:{picked:void 0,action:i.value}},confirm:({title:t,message:i})=>e.dialogs.confirm({title:t,description:i}),alert:({title:t,message:i})=>e.dialogs.alert({title:t,description:i}),table:({title:t,lines:i})=>new Promise(n=>{e.dialogs.open(o=>a.createComponent(Je,{host:e,title:t,lines:i,close:()=>{o.close()},done:n}))}),toast:(t,i)=>{e.toast.show({title:t,variant:i})}}}function U(e){const t=e.sessions.current();return(t==null?void 0:t.server.builtin)===!0?t:void 0}function W(e){var i,n;const t=(n=(i=U(e))==null?void 0:i.location)==null?void 0:n.directory;return t===void 0?{}:{location:{directory:t}}}function Be(e){const t=e.client.rpc(x),i=()=>W(e);return{settings:()=>t["settings.get"](void 0,i()),setSettings:n=>t["settings.set"](n,i()),status:()=>t["status.get"](void 0,i()),receipts:()=>t["receipts.list"](void 0,i()),sessionMode:n=>t["session-mode.get"]({sessionID:n},i()).then(o=>o.mode),setSessionMode:(n,o)=>t["session-mode.set"]({sessionID:n,mode:o},i()),tierRequest:n=>t["tier-request.get"]({sessionID:n},i()).then(o=>o.tier),setTierRequest:(n,o)=>t["tier-request.set"]({sessionID:n,tier:o},i())}}function Xe(e){const t=e.servers.list().map(n=>e.servers.get(n)).find(n=>(n==null?void 0:n.builtin)===!0);return t===void 0?void 0:t.data.location.model}async function Ze(e){const t=Ye(e),i=U(e),n=Xe(e);await(async()=>{await(n==null?void 0:n.sync(i==null?void 0:i.location).catch(()=>{})),await Pe({api:Be(e),view:t,models:()=>((n==null?void 0:n.list(i==null?void 0:i.location))??[]).map(r=>({providerID:r.providerID,modelID:r.modelID,name:r.name,variants:r.variants.map(s=>s.id)})),...i!==void 0?{sessionID:i.id}:{}})})().catch(r=>{t.toast(F(r),"error")})}const Ke="connection";function Qe(e){if(e===void 0)return[{id:Ke,label:"Server",value:"disconnected",tone:"warning"}];const t=[{id:"mode",label:"Mode",value:e.mode},{id:"pin",label:"Pin",value:e.pin===null?"none":f(e.pin)}];for(const i of w){const n=e.tiers[i],o=n.length===0?"empty":n.map(r=>`${f(r.ref)}${L(r)}`).join(", ");t.push({id:`tier-${i}`,label:`Tier ${i}`,value:o})}return t.push({id:"parked",label:"Parked",value:String(e.parked)}),t.push({id:"receipts",label:"Receipts",value:String(e.receipts)}),t}function et(e){return E.map(t=>({value:t,title:t===e?`${t} (current)`:t}))}function tt(e){return E.find(t=>t===e)}function it(e){return e.map(t=>H(t))}var nt=a.template("<span class=adaptive-saved role=status>Saved"),ot=a.template("<div class=adaptive-receipts>"),rt=a.template('<div class=adaptive-page><table class=adaptive-status aria-label="OpenCode Adaptive status"><tbody></tbody></table><label class=adaptive-mode><span class=adaptive-row-label>Mode</span><select class=adaptive-mode-select>'),st=a.template("<tr class=adaptive-row><td class=adaptive-row-label></td><td class=adaptive-row-value>"),at=a.template("<option>"),ct=a.template("<div class=adaptive-receipt-line>");const ut=5;function z(e){const[t,i]=m.createSignal(void 0),[n,o]=m.createSignal("observe"),[r,s]=m.createSignal([]),[c,p]=m.createSignal(!1),d=()=>{e.api.status().then(u=>{i(u),u!==void 0&&o(u.mode)}),e.api.receipts().then(u=>{s(u??[])})};m.onCleanup(e.api.watchSettings(d)),d();const g=u=>{const k=tt(u.value);u.value=n(),k!==void 0&&e.api.update({mode:k}).then(R=>{p(R),d()})};return(()=>{var u=rt(),k=u.firstChild,R=k.firstChild,J=k.nextSibling,Y=J.firstChild,A=Y.nextSibling;return a.insert(R,a.createComponent(m.For,{get each(){return Qe(t())},children:h=>(()=>{var $=st(),I=$.firstChild,B=I.nextSibling;return a.insert(I,()=>h.label),a.insert(B,()=>h.value),a.effect(()=>a.setAttribute($,"data-tone",h.tone)),$})()})),A.addEventListener("change",h=>{g(h.currentTarget)}),a.insert(A,a.createComponent(m.For,{get each(){return et(n())},children:h=>(()=>{var $=at();return a.insert($,()=>h.title),a.effect(()=>$.value=h.value),$})()})),a.insert(u,a.createComponent(m.Show,{get when(){return c()},get children(){return nt()}}),null),a.insert(u,a.createComponent(m.Show,{get when(){return t()!==void 0},get children(){var h=ot();return a.insert(h,a.createComponent(m.For,{get each(){return it(r()).slice(0,ut)},children:$=>(()=>{var I=ct();return a.insert(I,$),I})()})),h}}),null),a.effect(()=>A.disabled=t()===void 0),a.effect(()=>A.value=n()),u})()}const dt=O.Plugin.define({setup(e){const t=te(e.client,()=>W(e));e.add(O.SettingsPage,{id:"adaptive",title:"OpenCode Adaptive",entries:[{id:"adaptive",title:"OpenCode Adaptive",description:"Execution tier routing, operating mode and resource health"}],render:()=>a.createComponent(z,{api:t})}),e.add(O.Command,{id:"open",title:"OpenCode Adaptive",group:"Adaptive",suggested:!0,slash:{name:"adaptive"},run:()=>Ze(e)})}});exports.AdaptiveStatusPage=z;exports.default=dt;
|
package/dist/gui.d.ts
CHANGED
|
@@ -10,7 +10,8 @@ export declare function AdaptiveStatusPage(props: {
|
|
|
10
10
|
}): JSX.Element;
|
|
11
11
|
/**
|
|
12
12
|
* GUI plugin entry (the `./gui-experimental` bundle default export). Thin adapter: registers a native
|
|
13
|
-
* SettingsPage and a palette command that
|
|
13
|
+
* SettingsPage and a palette/`/adaptive` slash command that runs the same dialog flow as the TUI (ui/flow); no
|
|
14
|
+
* routing logic lives here (ADP-034/035).
|
|
14
15
|
*/
|
|
15
16
|
declare const _default: Plugin.Definition<{}>;
|
|
16
17
|
export default _default;
|
package/dist/gui.js
CHANGED
|
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "solid-js/jsx-runtime";
|
|
|
2
2
|
import { Command, Plugin, SettingsPage } from "@opencode/plugin/gui";
|
|
3
3
|
import { For, Show, createSignal, onCleanup } from "solid-js";
|
|
4
4
|
import { adaptiveGuiApi } from "./ui/gui/api.js";
|
|
5
|
+
import { openAdaptive, rpcLocation } from "./ui/gui/flow.js";
|
|
5
6
|
import { modeChoices, parseModeValue, receiptLines, statusRows } from "./ui/gui/view.js";
|
|
6
7
|
const RECEIPTS_PREVIEW_LIMIT = 5;
|
|
7
8
|
/**
|
|
@@ -43,11 +44,12 @@ export function AdaptiveStatusPage(props) {
|
|
|
43
44
|
}
|
|
44
45
|
/**
|
|
45
46
|
* GUI plugin entry (the `./gui-experimental` bundle default export). Thin adapter: registers a native
|
|
46
|
-
* SettingsPage and a palette command that
|
|
47
|
+
* SettingsPage and a palette/`/adaptive` slash command that runs the same dialog flow as the TUI (ui/flow); no
|
|
48
|
+
* routing logic lives here (ADP-034/035).
|
|
47
49
|
*/
|
|
48
50
|
export default Plugin.define({
|
|
49
51
|
setup(context) {
|
|
50
|
-
const api = adaptiveGuiApi(context.client);
|
|
52
|
+
const api = adaptiveGuiApi(context.client, () => rpcLocation(context));
|
|
51
53
|
context.add(SettingsPage, {
|
|
52
54
|
id: "adaptive",
|
|
53
55
|
title: "OpenCode Adaptive",
|
|
@@ -64,9 +66,9 @@ export default Plugin.define({
|
|
|
64
66
|
id: "open",
|
|
65
67
|
title: "OpenCode Adaptive",
|
|
66
68
|
group: "Adaptive",
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
suggested: true,
|
|
70
|
+
slash: { name: "adaptive" },
|
|
71
|
+
run: () => openAdaptive(context),
|
|
70
72
|
});
|
|
71
73
|
},
|
|
72
74
|
});
|
package/dist/gui.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gui.js","sourceRoot":"","sources":["../src/gui.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"gui.js","sourceRoot":"","sources":["../src/gui.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Command, Plugin, SettingsPage, type Context } from \"@opencode/plugin/gui\"\nimport { For, Show, createSignal, onCleanup } from \"solid-js\"\nimport type { JSX } from \"solid-js\"\n\nimport type { OperatingMode } from \"./core/scheduler/types.js\"\nimport type { Receipt } from \"./state/receipts/index.js\"\nimport type { StatusSnapshot } from \"./rpc.js\"\nimport { adaptiveGuiApi, type GuiApi } from \"./ui/gui/api.js\"\nimport { openAdaptive, rpcLocation } from \"./ui/gui/flow.js\"\nimport { modeChoices, parseModeValue, receiptLines, statusRows } from \"./ui/gui/view.js\"\n\nconst RECEIPTS_PREVIEW_LIMIT = 5\n\n/**\n * Native GUI settings page body (ADP-034). All data comes from the typed RPC adapter; the component holds no\n * local settings state. A failed read renders the disconnected row instead of stale data.\n */\nexport function AdaptiveStatusPage(props: { api: GuiApi }): JSX.Element {\n const [snapshot, setSnapshot] = createSignal<StatusSnapshot | undefined>(undefined)\n const [mode, setMode] = createSignal<OperatingMode>(\"observe\")\n const [receipts, setReceipts] = createSignal<Receipt[]>([])\n const [saved, setSaved] = createSignal(false)\n\n const refresh = (): void => {\n void props.api.status().then((next) => {\n setSnapshot(next)\n if (next !== undefined) setMode(next.mode)\n })\n void props.api.receipts().then((next) => {\n setReceipts(next ?? [])\n })\n }\n\n onCleanup(props.api.watchSettings(refresh))\n refresh()\n\n const pick = (select: HTMLSelectElement): void => {\n const value = parseModeValue(select.value)\n // The DOM changes before onChange, even if the signal stays unchanged\n // after a rejected write. Keep the control on the confirmed server value.\n select.value = mode()\n if (value === undefined) return\n void props.api.update({ mode: value }).then((ok) => {\n setSaved(ok)\n refresh()\n })\n }\n\n return (\n <div class=\"adaptive-page\">\n <table class=\"adaptive-status\" aria-label=\"OpenCode Adaptive status\">\n <tbody>\n <For each={statusRows(snapshot())}>\n {(row) => (\n <tr class=\"adaptive-row\" data-tone={row.tone}>\n <td class=\"adaptive-row-label\">{row.label}</td>\n <td class=\"adaptive-row-value\">{row.value}</td>\n </tr>\n )}\n </For>\n </tbody>\n </table>\n <label class=\"adaptive-mode\">\n <span class=\"adaptive-row-label\">Mode</span>\n <select\n class=\"adaptive-mode-select\"\n disabled={snapshot() === undefined}\n value={mode()}\n onChange={(event) => {\n pick(event.currentTarget)\n }}\n >\n <For each={modeChoices(mode())}>\n {(choice) => <option value={choice.value}>{choice.title}</option>}\n </For>\n </select>\n </label>\n <Show when={saved()}>\n <span class=\"adaptive-saved\" role=\"status\">\n Saved\n </span>\n </Show>\n <Show when={snapshot() !== undefined}>\n <div class=\"adaptive-receipts\">\n <For each={receiptLines(receipts()).slice(0, RECEIPTS_PREVIEW_LIMIT)}>\n {(line) => <div class=\"adaptive-receipt-line\">{line}</div>}\n </For>\n </div>\n </Show>\n </div>\n )\n}\n\n/**\n * GUI plugin entry (the `./gui-experimental` bundle default export). Thin adapter: registers a native\n * SettingsPage and a palette/`/adaptive` slash command that runs the same dialog flow as the TUI (ui/flow); no\n * routing logic lives here (ADP-034/035).\n */\nexport default Plugin.define({\n setup(context: Context): void {\n const api = adaptiveGuiApi(context.client, () => rpcLocation(context))\n context.add(SettingsPage, {\n id: \"adaptive\",\n title: \"OpenCode Adaptive\",\n entries: [\n {\n id: \"adaptive\",\n title: \"OpenCode Adaptive\",\n description: \"Execution tier routing, operating mode and resource health\",\n },\n ],\n render: () => <AdaptiveStatusPage api={api} />,\n })\n context.add(Command, {\n id: \"open\",\n title: \"OpenCode Adaptive\",\n group: \"Adaptive\",\n suggested: true,\n slash: { name: \"adaptive\" },\n run: () => openAdaptive(context),\n })\n },\n})\n"]}
|
package/dist/tui.js
CHANGED
|
@@ -18,8 +18,9 @@ import { createStore } from "solid-js/store";
|
|
|
18
18
|
import { PLUGIN_ID } from "./plugin-id.js";
|
|
19
19
|
import { adaptiveRpc } from "./rpc/definition.js";
|
|
20
20
|
import { parseMode } from "./state/settings/index.js";
|
|
21
|
-
import { runAdaptive } from "./ui/
|
|
22
|
-
import { errorText } from "./ui/
|
|
21
|
+
import { runAdaptive } from "./ui/flow/controller.js";
|
|
22
|
+
import { errorText } from "./ui/flow/options.js";
|
|
23
|
+
import { matchOptions } from "./ui/tui/search.js";
|
|
23
24
|
/**
|
|
24
25
|
* Label/color mapping for the footer chip. Pure so the mapping is testable
|
|
25
26
|
* without a JSX runtime: active is green (supervision engaged), observe is
|
|
@@ -57,7 +58,8 @@ export default define({
|
|
|
57
58
|
setTierRequest: (sessionID, tier) => client["tier-request.set"]({ sessionID, tier }, at()),
|
|
58
59
|
};
|
|
59
60
|
const view = {
|
|
60
|
-
|
|
61
|
+
// A placeholder marks a searchable list (the model picker).
|
|
62
|
+
select: (options) => context.ui.dialog.select(options.placeholder === undefined ? options : { ...options, search: matchOptions }),
|
|
61
63
|
selectPaged: (spec) =>
|
|
62
64
|
// Footer actions flip pages: triggering one closes the dialog and
|
|
63
65
|
// resolves with the action instead of a pick; the controller loop
|
package/dist/tui.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tui.js","sourceRoot":"","sources":["../src/tui.tsx"],"names":[],"mappings":"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","sourcesContent":["/** @jsxImportSource @opentui/solid */\n// The pragma pins the JSX runtime for this file. The host compiles plugin\n// sources in a cache dir where its loader's tsconfig lookup finds nothing and\n// its default (react/jsx-runtime) fails to resolve there (empirical, sibling\n// @coool plugins); our own tsc build reads the same pragma, so dist/tui.js\n// imports @opentui/solid/jsx-runtime — the renderer the slot render runs on.\n/**\n * TUI entrypoint (ADP-033): registers the `/adaptive` slash command and\n * drives the dialog-based flows through typed RPC, plus the footer status\n * chip rendered through host UI slots. The TUI is a thin view — no local\n * settings state beyond the chip's reactive mode mirror; the server stays\n * the single source of truth. This file is the repo's one JSX surface.\n */\nimport { define } from \"@opencode/plugin/tui/plugin\"\nimport type { Context } from \"@opencode/plugin/tui/plugin\"\nimport { TextAttributes } from \"@opentui/core\"\nimport { createStore } from \"solid-js/store\"\nimport { PLUGIN_ID } from \"./plugin-id.js\"\nimport { adaptiveRpc } from \"./rpc/definition.js\"\nimport type { OperatingMode } from \"./core/scheduler/types.js\"\nimport { parseMode } from \"./state/settings/index.js\"\nimport { runAdaptive } from \"./ui/tui/controller.js\"\nimport type { AdaptiveApi, AdaptiveView } from \"./ui/tui/controller.js\"\nimport type { SelectOption } from \"./ui/tui/options.js\"\nimport { errorText } from \"./ui/tui/options.js\"\nimport type { ModelSummary } from \"./ui/tui/tiers.js\"\n\n/** Footer chip appearance: label plus foreground color (null = theme default). */\nexport interface Chip {\n readonly text: string\n readonly fg: string | null\n}\n\n/**\n * Label/color mapping for the footer chip. Pure so the mapping is testable\n * without a JSX runtime: active is green (supervision engaged), observe is\n * the neutral default (visible but calm), off is a soft red — noticeable,\n * but calmer than full-brightness ANSI red, which reads as an error.\n */\nexport function chipFor(mode: OperatingMode): Chip {\n switch (mode) {\n case \"active\":\n return { text: \"Adaptive · active\", fg: \"green\" }\n case \"observe\":\n return { text: \"Adaptive · observe\", fg: null }\n case \"off\":\n return { text: \"Adaptive · off\", fg: \"#e57373\" }\n }\n}\n\nconst BOOT_FETCH_RETRIES = 5\nconst BOOT_FETCH_RETRY_MS = 2000\n\nexport default define({\n id: PLUGIN_ID,\n setup(context: Context) {\n const client = context.client.rpc(adaptiveRpc)\n // RPC handlers resolve per location: a location-less call never reaches a\n // project-local plugin's handlers (\"RPC is unavailable\", verified live in\n // the sibling @coool plugins), so every call carries the TUI's location.\n const at = () => ({ location: context.location })\n const api: AdaptiveApi = {\n settings: () => client[\"settings.get\"](undefined, at()),\n setSettings: (patch) => client[\"settings.set\"](patch, at()),\n status: () => client[\"status.get\"](undefined, at()),\n receipts: () => client[\"receipts.list\"](undefined, at()),\n sessionMode: (sessionID) =>\n client[\"session-mode.get\"]({ sessionID }, at()).then((out) => out.mode),\n setSessionMode: (sessionID, mode) => client[\"session-mode.set\"]({ sessionID, mode }, at()),\n tierRequest: (sessionID) =>\n client[\"tier-request.get\"]({ sessionID }, at()).then((out) => out.tier),\n setTierRequest: (sessionID, tier) => client[\"tier-request.set\"]({ sessionID, tier }, at()),\n }\n const view: AdaptiveView = {\n select: (options) => context.ui.dialog.select(options),\n selectPaged: <Value,>(spec: {\n title: string\n options: readonly SelectOption<Value>[]\n actions: readonly { title: string; value: string; bind: string }[]\n }) =>\n // Footer actions flip pages: triggering one closes the dialog and\n // resolves with the action instead of a pick; the controller loop\n // then reopens the browser at the new offset. The host resolves its\n // own select promise undefined on the clear() — resolving our\n // promise first-come means the action result wins.\n new Promise<{ picked: Value | undefined; action: string | undefined }>(\n (resolve, reject) => {\n void context.ui.dialog\n .select({\n title: spec.title,\n options: spec.options,\n actions: spec.actions.map((action) => ({\n bind: action.bind,\n title: action.title,\n selection: \"none\" as const,\n onTrigger: () => {\n context.ui.dialog.clear()\n resolve({ picked: undefined, action: action.value })\n },\n })),\n })\n .then((picked) => {\n resolve({ picked, action: undefined })\n }, reject)\n },\n ),\n confirm: (options) => context.ui.dialog.confirm(options),\n alert: (options) => context.ui.dialog.alert(options),\n table: (spec) =>\n // The host has no table dialog; a custom dialog render gives the\n // column grid its own layout. The default dialog size is too narrow\n // for the column grid (rows wrap mid-token), so the dialog is\n // widened to xlarge right after it opens. Escape closes the dialog\n // and fires onClose, which resolves the view promise.\n new Promise<void>((resolve) => {\n context.ui.dialog.show(\n () => (\n // Styled to match the host's own dialogs (bold title, dim esc\n // hint). The host dialog panel already insets the content, so\n // the box adds no top padding of its own; a blank spacer row\n // separates the title row from the table.\n <box style={{ flexDirection: \"column\", paddingBottom: 1, paddingX: 2 }}>\n <box style={{ flexDirection: \"row\", justifyContent: \"space-between\" }}>\n <text style={{ attributes: TextAttributes.BOLD }}>{spec.title}</text>\n <text style={{ attributes: TextAttributes.DIM }}>esc</text>\n </box>\n <text> </text>\n {spec.lines.map((line) => (\n <text>{line}</text>\n ))}\n </box>\n ),\n () => {\n resolve()\n },\n )\n context.ui.dialog.set({ size: \"xlarge\" })\n }),\n toast: (message, variant) => {\n context.ui.toast.show({ message, variant })\n },\n }\n // Real OpenCode models for the tier editor search (ADP-033); structural\n // summaries keep the view models free of client types.\n const models = (): readonly ModelSummary[] => {\n const list = context.data.location.model.list(context.location)\n return (list ?? []).map((info) => ({\n providerID: info.providerID,\n modelID: info.modelID,\n name: info.name,\n variants: info.variants.map((variant) => variant.id),\n }))\n }\n context.keymap.layer(() => ({\n // \"global\" opts out of input-mode scoping: the default (\"base\") binds\n // the commands to one OpenCode input mode, which kept the palette from\n // matching them (the sibling @coool plugins register the same way).\n mode: \"global\",\n commands: [\n {\n id: \"coool.adaptive.menu\",\n title: \"Adaptive\",\n group: \"Adaptive\",\n palette: true,\n // Surface the command in the palette's suggested row: discovery was\n // the reason users never found the menu at all.\n suggested: true,\n slash: { name: \"adaptive\" },\n run: () => {\n // Inside a session the flows steer that session's mode override\n // (ADP-054); on home they patch the global mode.\n const current = context.ui.router.current()\n const sessionID = current.type === \"session\" ? current.sessionID : undefined\n void runAdaptive({\n api,\n view,\n models,\n ...(sessionID !== undefined ? { sessionID } : {}),\n }).catch((error: unknown) => {\n view.toast(errorText(error), \"error\")\n })\n },\n },\n ],\n }))\n\n // Footer chip state: a reactive mirror of the server's operating mode,\n // fetched once at boot and kept live via the settings-changed event, plus\n // the per-session overrides seen so far (ADP-054).\n const [chip, setChip] = createStore<{\n mode: OperatingMode | null\n sessions: Record<string, OperatingMode | null>\n }>({ mode: null, sessions: {} })\n\n const fetchMode = async (): Promise<boolean> => {\n try {\n const settings = await api.settings()\n const mode = parseMode(settings.mode)\n if (mode !== undefined) setChip(\"mode\", mode)\n return true\n } catch {\n return false\n }\n }\n\n /**\n * One session's override, cached so the chip never re-fetches per render.\n * A fetch failure caches `null` (follow global): the chip stays correct\n * for override-less sessions, and a later `session-mode-changed` event\n * refreshes sessions that do carry an override.\n */\n const fetchSessionMode = async (sessionID: string): Promise<void> => {\n try {\n setChip(\"sessions\", sessionID, await api.sessionMode(sessionID))\n } catch {\n setChip(\"sessions\", sessionID, null)\n }\n }\n\n const bootFetch = async (retriesLeft: number): Promise<void> => {\n if (await fetchMode()) return\n // The server plugin may not be registered yet at TUI boot; retry\n // quietly. Bounded (unlike the sibling plugins' unbounded retry) so the\n // timer loop provably terminates: after the cap the chip stays hidden\n // until the next settings-changed event refreshes it.\n if (retriesLeft > 0) {\n setTimeout(() => void bootFetch(retriesLeft - 1), BOOT_FETCH_RETRY_MS)\n }\n }\n\n const renderChip = (mode: OperatingMode | null) => {\n if (mode === null) return <text></text>\n const entry = chipFor(mode)\n // The accent-colored span cannot execute in-repo: the terminal renderer\n // cannot start headless and jsdom's HTML parser mangles the intrinsic\n // <text><span> template (null insert parent). The chipFor tests pin the\n // color mapping; the chip render test pins the plain observe shape.\n /* v8 ignore start */\n if (entry.fg === null) return <text>{entry.text}</text>\n return (\n <text>\n <span style={{ fg: entry.fg }}>{entry.text}</span>\n </text>\n )\n /* v8 ignore stop */\n }\n const chipNode = (sessionID: string | undefined) => {\n // On home (no session id) the chip shows the global mode; in a session\n // it shows the session's EFFECTIVE mode (override ?? global).\n if (sessionID === undefined) return renderChip(chip.mode)\n const override = chip.sessions[sessionID]\n if (override === undefined) {\n // First render for this session: fetch the override; the store write\n // re-renders this node with the fetched value. Until then the global\n // mode is shown.\n void fetchSessionMode(sessionID)\n return renderChip(chip.mode)\n }\n return renderChip(override ?? chip.mode)\n }\n // The home footer slot only exists on the home screen; sessions render\n // the prompt footer — its slot input carries the open session's id, so\n // the chip there shows that session's effective mode (ADP-054).\n const disposeSlots = [\n context.ui.slot({ append: \"home.footer.status\", render: () => chipNode(undefined) }),\n context.ui.slot({\n append: \"prompt.footer.status\",\n render: (input) => chipNode(input.sessionID),\n }),\n ]\n\n const disposeWatch = client.events.on(\"settings-changed\", () => {\n void fetchMode()\n // A global mode change re-renders override-less sessions reactively;\n // cached overrides are re-fetched so a stale mirror cannot stick.\n for (const sessionID of Object.keys(chip.sessions)) {\n void fetchSessionMode(sessionID)\n }\n })\n const disposeSessionWatch = client.events.on(\"session-mode-changed\", (event) => {\n // The event data crosses the wire as an untyped record: a malformed\n // payload is ignored (fail-open) instead of steering a bogus session.\n const sessionID = event.data.sessionID\n if (typeof sessionID === \"string\") void fetchSessionMode(sessionID)\n })\n\n void bootFetch(BOOT_FETCH_RETRIES)\n\n return () => {\n for (const dispose of disposeSlots) dispose()\n disposeWatch()\n disposeSessionWatch()\n }\n },\n})\n"]}
|
|
1
|
+
{"version":3,"file":"tui.js","sourceRoot":"","sources":["../src/tui.tsx"],"names":[],"mappings":"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","sourcesContent":["/** @jsxImportSource @opentui/solid */\n// The pragma pins the JSX runtime for this file. The host compiles plugin\n// sources in a cache dir where its loader's tsconfig lookup finds nothing and\n// its default (react/jsx-runtime) fails to resolve there (empirical, sibling\n// @coool plugins); our own tsc build reads the same pragma, so dist/tui.js\n// imports @opentui/solid/jsx-runtime — the renderer the slot render runs on.\n/**\n * TUI entrypoint (ADP-033): registers the `/adaptive` slash command and\n * drives the dialog-based flows through typed RPC, plus the footer status\n * chip rendered through host UI slots. The TUI is a thin view — no local\n * settings state beyond the chip's reactive mode mirror; the server stays\n * the single source of truth. This file is the repo's one JSX surface.\n */\nimport { define } from \"@opencode/plugin/tui/plugin\"\nimport type { Context } from \"@opencode/plugin/tui/plugin\"\nimport { TextAttributes } from \"@opentui/core\"\nimport { createStore } from \"solid-js/store\"\nimport { PLUGIN_ID } from \"./plugin-id.js\"\nimport { adaptiveRpc } from \"./rpc/definition.js\"\nimport type { OperatingMode } from \"./core/scheduler/types.js\"\nimport { parseMode } from \"./state/settings/index.js\"\nimport { runAdaptive } from \"./ui/flow/controller.js\"\nimport type { AdaptiveApi, AdaptiveView } from \"./ui/flow/controller.js\"\nimport { errorText } from \"./ui/flow/options.js\"\nimport type { ModelSummary } from \"./ui/flow/tiers.js\"\nimport { matchOptions } from \"./ui/tui/search.js\"\n\n/** Footer chip appearance: label plus foreground color (null = theme default). */\nexport interface Chip {\n readonly text: string\n readonly fg: string | null\n}\n\n/**\n * Label/color mapping for the footer chip. Pure so the mapping is testable\n * without a JSX runtime: active is green (supervision engaged), observe is\n * the neutral default (visible but calm), off is a soft red — noticeable,\n * but calmer than full-brightness ANSI red, which reads as an error.\n */\nexport function chipFor(mode: OperatingMode): Chip {\n switch (mode) {\n case \"active\":\n return { text: \"Adaptive · active\", fg: \"green\" }\n case \"observe\":\n return { text: \"Adaptive · observe\", fg: null }\n case \"off\":\n return { text: \"Adaptive · off\", fg: \"#e57373\" }\n }\n}\n\nconst BOOT_FETCH_RETRIES = 5\nconst BOOT_FETCH_RETRY_MS = 2000\n\nexport default define({\n id: PLUGIN_ID,\n setup(context: Context) {\n const client = context.client.rpc(adaptiveRpc)\n // RPC handlers resolve per location: a location-less call never reaches a\n // project-local plugin's handlers (\"RPC is unavailable\", verified live in\n // the sibling @coool plugins), so every call carries the TUI's location.\n const at = () => ({ location: context.location })\n const api: AdaptiveApi = {\n settings: () => client[\"settings.get\"](undefined, at()),\n setSettings: (patch) => client[\"settings.set\"](patch, at()),\n status: () => client[\"status.get\"](undefined, at()),\n receipts: () => client[\"receipts.list\"](undefined, at()),\n sessionMode: (sessionID) =>\n client[\"session-mode.get\"]({ sessionID }, at()).then((out) => out.mode),\n setSessionMode: (sessionID, mode) => client[\"session-mode.set\"]({ sessionID, mode }, at()),\n tierRequest: (sessionID) =>\n client[\"tier-request.get\"]({ sessionID }, at()).then((out) => out.tier),\n setTierRequest: (sessionID, tier) => client[\"tier-request.set\"]({ sessionID, tier }, at()),\n }\n const view: AdaptiveView = {\n // A placeholder marks a searchable list (the model picker).\n select: (options) =>\n context.ui.dialog.select(\n options.placeholder === undefined ? options : { ...options, search: matchOptions },\n ),\n selectPaged: <Value,>(spec: {\n title: string\n options: readonly { title: string; value: Value }[]\n actions: readonly { title: string; value: string; bind: string }[]\n }) =>\n // Footer actions flip pages: triggering one closes the dialog and\n // resolves with the action instead of a pick; the controller loop\n // then reopens the browser at the new offset. The host resolves its\n // own select promise undefined on the clear() — resolving our\n // promise first-come means the action result wins.\n new Promise<{ picked: Value | undefined; action: string | undefined }>(\n (resolve, reject) => {\n void context.ui.dialog\n .select({\n title: spec.title,\n options: spec.options,\n actions: spec.actions.map((action) => ({\n bind: action.bind,\n title: action.title,\n selection: \"none\" as const,\n onTrigger: () => {\n context.ui.dialog.clear()\n resolve({ picked: undefined, action: action.value })\n },\n })),\n })\n .then((picked) => {\n resolve({ picked, action: undefined })\n }, reject)\n },\n ),\n confirm: (options) => context.ui.dialog.confirm(options),\n alert: (options) => context.ui.dialog.alert(options),\n table: (spec) =>\n // The host has no table dialog; a custom dialog render gives the\n // column grid its own layout. The default dialog size is too narrow\n // for the column grid (rows wrap mid-token), so the dialog is\n // widened to xlarge right after it opens. Escape closes the dialog\n // and fires onClose, which resolves the view promise.\n new Promise<void>((resolve) => {\n context.ui.dialog.show(\n () => (\n // Styled to match the host's own dialogs (bold title, dim esc\n // hint). The host dialog panel already insets the content, so\n // the box adds no top padding of its own; a blank spacer row\n // separates the title row from the table.\n <box style={{ flexDirection: \"column\", paddingBottom: 1, paddingX: 2 }}>\n <box style={{ flexDirection: \"row\", justifyContent: \"space-between\" }}>\n <text style={{ attributes: TextAttributes.BOLD }}>{spec.title}</text>\n <text style={{ attributes: TextAttributes.DIM }}>esc</text>\n </box>\n <text> </text>\n {spec.lines.map((line) => (\n <text>{line}</text>\n ))}\n </box>\n ),\n () => {\n resolve()\n },\n )\n context.ui.dialog.set({ size: \"xlarge\" })\n }),\n toast: (message, variant) => {\n context.ui.toast.show({ message, variant })\n },\n }\n // Real OpenCode models for the tier editor search (ADP-033); structural\n // summaries keep the view models free of client types.\n const models = (): readonly ModelSummary[] => {\n const list = context.data.location.model.list(context.location)\n return (list ?? []).map((info) => ({\n providerID: info.providerID,\n modelID: info.modelID,\n name: info.name,\n variants: info.variants.map((variant) => variant.id),\n }))\n }\n context.keymap.layer(() => ({\n // \"global\" opts out of input-mode scoping: the default (\"base\") binds\n // the commands to one OpenCode input mode, which kept the palette from\n // matching them (the sibling @coool plugins register the same way).\n mode: \"global\",\n commands: [\n {\n id: \"coool.adaptive.menu\",\n title: \"Adaptive\",\n group: \"Adaptive\",\n palette: true,\n // Surface the command in the palette's suggested row: discovery was\n // the reason users never found the menu at all.\n suggested: true,\n slash: { name: \"adaptive\" },\n run: () => {\n // Inside a session the flows steer that session's mode override\n // (ADP-054); on home they patch the global mode.\n const current = context.ui.router.current()\n const sessionID = current.type === \"session\" ? current.sessionID : undefined\n void runAdaptive({\n api,\n view,\n models,\n ...(sessionID !== undefined ? { sessionID } : {}),\n }).catch((error: unknown) => {\n view.toast(errorText(error), \"error\")\n })\n },\n },\n ],\n }))\n\n // Footer chip state: a reactive mirror of the server's operating mode,\n // fetched once at boot and kept live via the settings-changed event, plus\n // the per-session overrides seen so far (ADP-054).\n const [chip, setChip] = createStore<{\n mode: OperatingMode | null\n sessions: Record<string, OperatingMode | null>\n }>({ mode: null, sessions: {} })\n\n const fetchMode = async (): Promise<boolean> => {\n try {\n const settings = await api.settings()\n const mode = parseMode(settings.mode)\n if (mode !== undefined) setChip(\"mode\", mode)\n return true\n } catch {\n return false\n }\n }\n\n /**\n * One session's override, cached so the chip never re-fetches per render.\n * A fetch failure caches `null` (follow global): the chip stays correct\n * for override-less sessions, and a later `session-mode-changed` event\n * refreshes sessions that do carry an override.\n */\n const fetchSessionMode = async (sessionID: string): Promise<void> => {\n try {\n setChip(\"sessions\", sessionID, await api.sessionMode(sessionID))\n } catch {\n setChip(\"sessions\", sessionID, null)\n }\n }\n\n const bootFetch = async (retriesLeft: number): Promise<void> => {\n if (await fetchMode()) return\n // The server plugin may not be registered yet at TUI boot; retry\n // quietly. Bounded (unlike the sibling plugins' unbounded retry) so the\n // timer loop provably terminates: after the cap the chip stays hidden\n // until the next settings-changed event refreshes it.\n if (retriesLeft > 0) {\n setTimeout(() => void bootFetch(retriesLeft - 1), BOOT_FETCH_RETRY_MS)\n }\n }\n\n const renderChip = (mode: OperatingMode | null) => {\n if (mode === null) return <text></text>\n const entry = chipFor(mode)\n // The accent-colored span cannot execute in-repo: the terminal renderer\n // cannot start headless and jsdom's HTML parser mangles the intrinsic\n // <text><span> template (null insert parent). The chipFor tests pin the\n // color mapping; the chip render test pins the plain observe shape.\n /* v8 ignore start */\n if (entry.fg === null) return <text>{entry.text}</text>\n return (\n <text>\n <span style={{ fg: entry.fg }}>{entry.text}</span>\n </text>\n )\n /* v8 ignore stop */\n }\n const chipNode = (sessionID: string | undefined) => {\n // On home (no session id) the chip shows the global mode; in a session\n // it shows the session's EFFECTIVE mode (override ?? global).\n if (sessionID === undefined) return renderChip(chip.mode)\n const override = chip.sessions[sessionID]\n if (override === undefined) {\n // First render for this session: fetch the override; the store write\n // re-renders this node with the fetched value. Until then the global\n // mode is shown.\n void fetchSessionMode(sessionID)\n return renderChip(chip.mode)\n }\n return renderChip(override ?? chip.mode)\n }\n // The home footer slot only exists on the home screen; sessions render\n // the prompt footer — its slot input carries the open session's id, so\n // the chip there shows that session's effective mode (ADP-054).\n const disposeSlots = [\n context.ui.slot({ append: \"home.footer.status\", render: () => chipNode(undefined) }),\n context.ui.slot({\n append: \"prompt.footer.status\",\n render: (input) => chipNode(input.sessionID),\n }),\n ]\n\n const disposeWatch = client.events.on(\"settings-changed\", () => {\n void fetchMode()\n // A global mode change re-renders override-less sessions reactively;\n // cached overrides are re-fetched so a stale mirror cannot stick.\n for (const sessionID of Object.keys(chip.sessions)) {\n void fetchSessionMode(sessionID)\n }\n })\n const disposeSessionWatch = client.events.on(\"session-mode-changed\", (event) => {\n // The event data crosses the wire as an untyped record: a malformed\n // payload is ignored (fail-open) instead of steering a bogus session.\n const sessionID = event.data.sessionID\n if (typeof sessionID === \"string\") void fetchSessionMode(sessionID)\n })\n\n void bootFetch(BOOT_FETCH_RETRIES)\n\n return () => {\n for (const dispose of disposeSlots) dispose()\n disposeWatch()\n disposeSessionWatch()\n }\n },\n})\n"]}
|
|
@@ -25,14 +25,15 @@ export interface AdaptiveView {
|
|
|
25
25
|
title: string;
|
|
26
26
|
placeholder?: string;
|
|
27
27
|
options: readonly SelectOption<Value>[];
|
|
28
|
-
current?: Value;
|
|
29
|
-
search?: (query: string, options: readonly SelectOption<Value>[]) => readonly SelectOption<Value>[];
|
|
30
28
|
}): Promise<Value | undefined>;
|
|
31
29
|
/** A select with footer actions (page flips): the action closes the
|
|
32
30
|
* dialog and is reported separately from an option pick. */
|
|
33
31
|
selectPaged<Value>(spec: {
|
|
34
32
|
title: string;
|
|
35
|
-
options: readonly
|
|
33
|
+
options: readonly {
|
|
34
|
+
title: string;
|
|
35
|
+
value: Value;
|
|
36
|
+
}[];
|
|
36
37
|
actions: readonly {
|
|
37
38
|
title: string;
|
|
38
39
|
value: string;
|
|
@@ -45,10 +46,6 @@ export interface AdaptiveView {
|
|
|
45
46
|
confirm(options: {
|
|
46
47
|
title: string;
|
|
47
48
|
message: string;
|
|
48
|
-
label?: {
|
|
49
|
-
confirm?: string;
|
|
50
|
-
cancel?: string;
|
|
51
|
-
};
|
|
52
49
|
}): Promise<boolean | undefined>;
|
|
53
50
|
alert(options: {
|
|
54
51
|
title: string;
|
|
@@ -59,7 +56,7 @@ export interface AdaptiveView {
|
|
|
59
56
|
title: string;
|
|
60
57
|
lines: string[];
|
|
61
58
|
}): Promise<void>;
|
|
62
|
-
toast(message: string, variant: "
|
|
59
|
+
toast(message: string, variant: "success" | "error"): void;
|
|
63
60
|
}
|
|
64
61
|
export interface AdaptiveApi {
|
|
65
62
|
settings: () => Promise<SettingsV1>;
|
|
@@ -291,7 +291,7 @@ async function runTierEditor(env, feedback) {
|
|
|
291
291
|
}
|
|
292
292
|
}
|
|
293
293
|
else {
|
|
294
|
-
const next = await runResourceMenu(env, pageSettings, tier, choice
|
|
294
|
+
const next = await runResourceMenu(env, pageSettings, tier, choice, feedback);
|
|
295
295
|
if (next !== undefined) {
|
|
296
296
|
saved = next;
|
|
297
297
|
pageSettings = next;
|
|
@@ -319,13 +319,11 @@ async function runAddModel(env, settings, tier, feedback) {
|
|
|
319
319
|
const pick = await env.view.select({
|
|
320
320
|
title: "Add model",
|
|
321
321
|
placeholder: "Search provider / model / name",
|
|
322
|
-
options: modelPickerOptions(models
|
|
323
|
-
search: (query) => modelPickerOptions(models, query),
|
|
322
|
+
options: modelPickerOptions(models),
|
|
324
323
|
});
|
|
325
324
|
if (pick === undefined)
|
|
326
325
|
return undefined;
|
|
327
|
-
const
|
|
328
|
-
const variants = info?.variants ?? [];
|
|
326
|
+
const variants = pick.variants;
|
|
329
327
|
let variant;
|
|
330
328
|
if (variants.length > 0) {
|
|
331
329
|
const choice = await env.view.select({
|
|
@@ -350,10 +348,7 @@ function resourceMenuOptions() {
|
|
|
350
348
|
{ title: "Remove", value: "remove" },
|
|
351
349
|
];
|
|
352
350
|
}
|
|
353
|
-
async function runResourceMenu(env, settings, tier, index, feedback) {
|
|
354
|
-
const ref = settings.tiers[tier][index];
|
|
355
|
-
if (ref === undefined)
|
|
356
|
-
return undefined;
|
|
351
|
+
async function runResourceMenu(env, settings, tier, { index, ref }, feedback) {
|
|
357
352
|
const pick = await env.view.select({
|
|
358
353
|
title: `Resource: ${resourceLabel(ref)}`,
|
|
359
354
|
options: resourceMenuOptions(),
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controller.js","sourceRoot":"","sources":["../../../src/ui/flow/controller.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The `/adaptive` keyboard-flow controller (ADP-033): a pure-ish state machine\n * over two seams — the typed RPC API and the host dialogs/toasts. It holds no\n * local settings state: every mutation goes through `settings.set` and the\n * returned snapshot becomes the new local view (the server is the single\n * source of truth, ADP-036).\n *\n * Flow contract: `undefined` selections always mean \"cancel this step\" — the\n * menu loop continues or exits; API failures toast and keep the loop alive.\n */\nimport type { OperatingMode } from \"../../core/scheduler/types.js\"\nimport type { TierName } from \"../../core/tier-policy/types.js\"\nimport { TIERS } from \"../../core/tier-policy/types.js\"\nimport type { ModelRef } from \"../../core/work-unit/types.js\"\nimport type { SettingsPatch, SettingsV1 } from \"../../state/settings/index.js\"\nimport type { PatchResult } from \"../../state/settings/types.js\"\nimport type { SessionModeSetResult } from \"../../state/session-modes/index.js\"\nimport type { TierRequestSetResult } from \"../../rpc/handlers.js\"\nimport type { Receipt } from \"../../state/receipts/types.js\"\nimport type { StatusSnapshot } from \"../../rpc/status.js\"\nimport { STATUS_EMPTY_MESSAGE, statusTable } from \"./status.js\"\nimport {\n mainMenuOptions,\n modeOptions,\n modeTargetOptions,\n sessionModeOptions,\n sessionTierOptions,\n type MainMenuChoice,\n} from \"./menu.js\"\nimport {\n modelPickerOptions,\n moveResource,\n moveResourceToTop,\n removeResource,\n resourceLabel,\n tierEditorOptions,\n tierMenuOptions,\n variantOptions,\n type ModelSummary,\n} from \"./tiers.js\"\nimport { errorText, type SelectOption } from \"./options.js\"\nimport { receiptDetail, receiptLine, receiptPage, RECEIPTS_PAGE_SIZE } from \"./receipts.js\"\n\nexport interface AdaptiveView {\n select<Value>(options: {\n title: string\n placeholder?: string\n options: readonly SelectOption<Value>[]\n }): Promise<Value | undefined>\n /** A select with footer actions (page flips): the action closes the\n * dialog and is reported separately from an option pick. */\n selectPaged<Value>(spec: {\n title: string\n options: readonly { title: string; value: Value }[]\n actions: readonly { title: string; value: string; bind: string }[]\n }): Promise<{ picked: Value | undefined; action: string | undefined }>\n confirm(options: { title: string; message: string }): Promise<boolean | undefined>\n alert(options: { title: string; message: string }): Promise<void>\n /** A column-aligned text table (header row first); Escape closes it. */\n table(spec: { title: string; lines: string[] }): Promise<void>\n toast(message: string, variant: \"success\" | \"error\"): void\n}\n\nexport interface AdaptiveApi {\n settings: () => Promise<SettingsV1>\n setSettings: (patch: SettingsPatch) => Promise<PatchResult>\n status: () => Promise<StatusSnapshot>\n receipts: () => Promise<Receipt[]>\n /** The session's mode override (`null` = follows the global mode). */\n sessionMode: (sessionID: string) => Promise<OperatingMode | null>\n setSessionMode: (sessionID: string, mode: OperatingMode | null) => Promise<SessionModeSetResult>\n /** The session's pinned tier request (`null` = follows the routing ladder). */\n tierRequest: (sessionID: string) => Promise<TierName | null>\n setTierRequest: (sessionID: string, tier: string | null) => Promise<TierRequestSetResult>\n}\n\nexport type ModelLister = () => readonly ModelSummary[]\n\nexport interface AdaptiveEnv {\n api: AdaptiveApi\n view: AdaptiveView\n models: ModelLister\n /**\n * The session the command was opened from (ADP-054). Present only when the\n * palette command ran inside a session screen — the mode menu then steers\n * that session's override instead of the global mode.\n */\n sessionID?: string | undefined\n}\n\nexport const INVITE_TITLE = \"OpenCode Adaptive\"\nexport const INVITE_MESSAGE =\n \"Adaptive starts in Observe mode: routing decisions are recorded as receipts, \" +\n \"but no model is switched. Open /adaptive to configure tiers or switch to Active.\"\n\n/** Tracks whether this dialog session already showed the save toast. */\nexport interface SaveFeedback {\n once: boolean\n}\n\n/** Run one API call; on failure toast and return undefined. */\nasync function tryApi<T>(env: AdaptiveEnv, run: () => Promise<T>): Promise<T | undefined> {\n try {\n return await run()\n } catch (error) {\n env.view.toast(errorText(error), \"error\")\n return undefined\n }\n}\n\n/**\n * Apply one patch; alert on validation errors, toast on success. The host\n * never auto-dismisses toasts, so the success toast is shown only on the\n * first successful save of a dialog session — later saves stay silent\n * (the updated menu already reflects them).\n */\nasync function applySettings(\n env: AdaptiveEnv,\n patch: SettingsPatch,\n feedback: SaveFeedback,\n): Promise<SettingsV1 | undefined> {\n const result = await tryApi(env, () => env.api.setSettings(patch))\n if (result === undefined) return undefined\n if (!result.ok) {\n await env.view.alert({ title: \"Invalid settings\", message: result.errors.join(\"; \") })\n return undefined\n }\n if (!feedback.once) {\n env.view.toast(\"Settings saved\", \"success\")\n feedback.once = true\n }\n return result.settings\n}\n\n/**\n * Persist one session-mode override; alert on errors, toast on success. Same\n * once-toast semantics as `applySettings` — the success toast is shown only\n * on the first successful save of a dialog session.\n */\nasync function applySessionMode(\n env: AdaptiveEnv,\n sessionID: string,\n mode: OperatingMode | null,\n feedback: SaveFeedback,\n): Promise<SessionModeSetResult | undefined> {\n const result = await tryApi(env, () => env.api.setSessionMode(sessionID, mode))\n if (result === undefined) return undefined\n if (!result.ok) {\n await env.view.alert({ title: \"Invalid settings\", message: result.errors.join(\"; \") })\n return undefined\n }\n if (!feedback.once) {\n env.view.toast(\"Settings saved\", \"success\")\n feedback.once = true\n }\n return result\n}\n\n/**\n * Persist one session tier request; alert on errors, toast on success. Same\n * once-toast semantics as `applySettings` — the success toast is shown only\n * on the first successful save of a dialog session.\n */\nasync function applyTierRequest(\n env: AdaptiveEnv,\n sessionID: string,\n tier: string | null,\n feedback: SaveFeedback,\n): Promise<TierRequestSetResult | undefined> {\n const result = await tryApi(env, () => env.api.setTierRequest(sessionID, tier))\n if (result === undefined) return undefined\n if (!result.ok) {\n await env.view.alert({ title: \"Invalid tier request\", message: result.errors.join(\"; \") })\n return undefined\n }\n if (!feedback.once) {\n env.view.toast(\"Settings saved\", \"success\")\n feedback.once = true\n }\n return result\n}\n\nexport async function runAdaptive(env: AdaptiveEnv): Promise<void> {\n const loaded = await tryApi(env, () => env.api.settings())\n if (loaded === undefined) return\n let settings = loaded\n const feedback: SaveFeedback = { once: false }\n const sessionID = env.sessionID\n // The session's current override: fetched once when the flow starts inside\n // a session; a failure degrades to \"follow global\" (the menu still works).\n let override: OperatingMode | null =\n sessionID === undefined\n ? null\n : ((await tryApi(env, () => env.api.sessionMode(sessionID))) ?? null)\n if (!settings.inviteDismissed) {\n await env.view.confirm({ title: INVITE_TITLE, message: INVITE_MESSAGE })\n const pick = await env.view.select({\n title: INVITE_TITLE,\n options: [\n { title: \"Open the menu now\", value: true },\n { title: \"Later\", value: false },\n ],\n })\n const dismissed = await applySettings(env, { inviteDismissed: true }, feedback)\n if (dismissed !== undefined) settings = dismissed\n if (pick !== true) return\n }\n for (;;) {\n // Fresh status and settings on every pass: the menu rows pair the two,\n // and the server is the single source of truth (ADP-036). A failed read\n // degrades silently to the last known values — the menu descriptions\n // must not spam a toast on every loop iteration (the flows that need\n // status toast through tryApi themselves).\n const [freshSettings, status] = await Promise.all([\n env.api.settings().catch(() => undefined),\n env.api.status().catch(() => undefined),\n ])\n if (freshSettings !== undefined) settings = freshSettings\n // The session's pinned tier request: fetched per loop so the entry\n // description tracks the latest request; a failure degrades to the\n // generic description (the menu still works).\n const tierPinned =\n sessionID === undefined\n ? undefined\n : await env.api.tierRequest(sessionID).catch(() => undefined)\n const choice = await env.view.select({\n title: `Adaptive (${override ?? settings.mode})`,\n options: mainMenuOptions(settings, status, tierPinned, override ?? settings.mode),\n })\n if (choice === undefined) return\n if (choice === \"status\") {\n await runStatusView(env)\n continue\n }\n if (choice === \"models\") {\n settings = (await runTierEditor(env, feedback)) ?? settings\n continue\n }\n if (choice === \"mode\") {\n const next = await runModeMenu(env, settings, feedback, sessionID, override)\n if (next !== undefined) {\n settings = next.settings\n override = next.override\n }\n continue\n }\n if (choice === \"tier\") {\n if (sessionID === undefined) {\n // The tier request only exists inside a session; on home the pick is\n // explained (not silently dropped) and the menu re-opens.\n await env.view.alert({\n title: \"This session\",\n message:\n \"This session's tier request can only be changed inside a session. Open a session and run /adaptive from there.\",\n })\n continue\n }\n await runTierRequestMenu(env, sessionID, feedback)\n continue\n }\n if (choice === \"pin\") {\n settings = (await runPinMenu(env, settings, feedback)) ?? settings\n continue\n }\n await runReceiptsView(env)\n }\n}\n\n/** What one mode-menu pass changed (settings and/or the session override). */\ninterface ModeMenuOutcome {\n settings: SettingsV1\n override: OperatingMode | null\n}\n\nasync function runModeMenu(\n env: AdaptiveEnv,\n settings: SettingsV1,\n feedback: SaveFeedback,\n sessionID: string | undefined,\n override: OperatingMode | null,\n): Promise<ModeMenuOutcome | undefined> {\n // Every screen gets the same first question — which mode to steer — so a\n // user who opens the menu on home learns the two-level structure before\n // they ever run the command inside a session.\n for (;;) {\n const target = await env.view.select({\n title: \"Operating mode\",\n options: modeTargetOptions(settings.mode, override ?? settings.mode),\n })\n if (target === undefined) return undefined\n if (target === \"global\") {\n const pick = await env.view.select({\n title: \"Global mode\",\n options: modeOptions(settings.mode),\n })\n if (pick === undefined || pick === settings.mode) return undefined\n const applied = await applySettings(env, { mode: pick }, feedback)\n // The session override is deliberately untouched: changing the global\n // default must not rewrite what a session was pinned to.\n return applied === undefined ? undefined : { settings: applied, override }\n }\n if (sessionID === undefined) {\n // The session override only exists inside a session; on home the pick\n // is explained (not silently dropped) and the target question repeats.\n await env.view.alert({\n title: \"This session\",\n message:\n \"This session's mode can only be changed inside a session. Open a session and run /adaptive from there.\",\n })\n continue\n }\n const pick = await env.view.select({\n title: \"This session's mode\",\n options: sessionModeOptions({\n global: settings.mode,\n override,\n effective: override ?? settings.mode,\n }),\n })\n if (pick === undefined) return undefined\n if (pick.kind === \"global\") {\n if (override === null) return undefined\n const saved = await applySessionMode(env, sessionID, null, feedback)\n return saved === undefined ? undefined : { settings, override: null }\n }\n if (pick.mode === (override ?? settings.mode)) return undefined\n const saved = await applySessionMode(env, sessionID, pick.mode, feedback)\n return saved === undefined ? undefined : { settings, override: pick.mode }\n }\n}\n\n/** The per-session tier-request menu (ADP-055): pin a tier or clear it. */\nasync function runTierRequestMenu(\n env: AdaptiveEnv,\n sessionID: string,\n feedback: SaveFeedback,\n): Promise<void> {\n const pinned = await tryApi(env, () => env.api.tierRequest(sessionID))\n if (pinned === undefined) return\n const pick = await env.view.select({\n title: \"This session's tier\",\n options: sessionTierOptions({ pinned }),\n })\n if (pick === undefined) return\n const next = pick.kind === \"ladder\" ? null : pick.tier\n if (next === pinned) return\n await applyTierRequest(env, sessionID, next, feedback)\n}\n\n/**\n * The status view: every configured model with its state as a column-aligned\n * table (header row, tier order, priority order within tiers). No\n * per-resource selection step — the table IS the view; Escape returns to the\n * menu.\n */\nasync function runStatusView(env: AdaptiveEnv): Promise<void> {\n const snapshot = await tryApi(env, () => env.api.status())\n if (snapshot === undefined) return\n const configured = TIERS.some((tier) => snapshot.tiers[tier].length > 0)\n if (!configured) {\n await env.view.alert({ title: \"Status\", message: STATUS_EMPTY_MESSAGE })\n return\n }\n await env.view.table({ title: \"Status\", lines: statusTable(snapshot) })\n}\n\ntype TierEditorOutcome = SettingsV1 | undefined\n\nasync function runTierEditor(env: AdaptiveEnv, feedback: SaveFeedback): Promise<TierEditorOutcome> {\n // The last confirmed server snapshot from this editor session, returned\n // when the editor closes so the menu view stays in sync with the server.\n let saved: SettingsV1 | undefined\n for (;;) {\n // The server is the single source of truth (ADP-036): settings and\n // status are re-fetched on every tier-list pass — entering the editor\n // and after each Escape out of a tier page — so an edit can never\n // clobber a change made outside this dialog. A failed read toasts and\n // closes the editor: the tier list cannot render meaningfully without\n // them.\n const settings = await tryApi(env, () => env.api.settings())\n if (settings === undefined) return saved\n const status = await tryApi(env, () => env.api.status())\n if (status === undefined) return saved\n const tier = await env.view.select({ title: \"Tier\", options: tierMenuOptions(status) })\n if (tier === undefined) return saved\n let pageStatus = status\n let pageSettings = settings\n // The tier page owns the loop until Escape: an add, move, or remove\n // re-opens this page instead of bouncing back to the tier list, so\n // consecutive edits on one tier stay in context. The page's settings\n // and status carry across iterations: each action's save response\n // updates them for the next render.\n for (;;) {\n const choice = await env.view.select({\n title: `Tier: ${tier}`,\n options: tierEditorOptions(pageSettings, tier, pageStatus),\n })\n if (choice === undefined) break\n if (choice.kind === \"add\") {\n const next = await runAddModel(env, pageSettings, tier, feedback)\n if (next !== undefined) {\n saved = next\n pageSettings = next\n }\n } else {\n const next = await runResourceMenu(env, pageSettings, tier, choice, feedback)\n if (next !== undefined) {\n saved = next\n pageSettings = next\n }\n }\n // The rows re-pair with the server after every action; a failed read\n // keeps the last known values so the page still renders (the save\n // already reported itself — a failed cosmetic refresh must neither\n // spam a toast nor bounce the page).\n const [freshSettings, freshStatus] = await Promise.all([\n env.api.settings().catch(() => undefined),\n env.api.status().catch(() => undefined),\n ])\n if (freshSettings !== undefined) {\n saved = freshSettings\n pageSettings = freshSettings\n }\n if (freshStatus !== undefined) pageStatus = freshStatus\n }\n }\n}\n\nasync function runAddModel(\n env: AdaptiveEnv,\n settings: SettingsV1,\n tier: TierName,\n feedback: SaveFeedback,\n): Promise<SettingsV1 | undefined> {\n const models = env.models()\n const pick = await env.view.select({\n title: \"Add model\",\n placeholder: \"Search provider / model / name\",\n options: modelPickerOptions(models),\n })\n if (pick === undefined) return undefined\n const variants = pick.variants\n let variant: string | undefined\n if (variants.length > 0) {\n const choice = await env.view.select({\n title: `Variant: ${pick.providerID}/${pick.modelID}`,\n options: variantOptions(variants),\n })\n if (choice === undefined) return undefined\n variant = choice.variant\n }\n const ref: ModelRef =\n variant === undefined\n ? { providerID: pick.providerID, id: pick.modelID }\n : { providerID: pick.providerID, id: pick.modelID, variant }\n const list = [...settings.tiers[tier], ref]\n return applySettings(env, { tiers: { ...settings.tiers, [tier]: list } }, feedback)\n}\n\ntype ResourceMenuChoice = \"top\" | \"up\" | \"down\" | \"remove\"\n\nfunction resourceMenuOptions(): SelectOption<ResourceMenuChoice>[] {\n return [\n { title: \"Move to top\", value: \"top\", description: \"Make this the primary resource\" },\n { title: \"Move up\", value: \"up\", description: \"Higher priority within the tier\" },\n { title: \"Move down\", value: \"down\", description: \"Lower priority within the tier\" },\n { title: \"Remove\", value: \"remove\" },\n ]\n}\n\nasync function runResourceMenu(\n env: AdaptiveEnv,\n settings: SettingsV1,\n tier: TierName,\n { index, ref }: { index: number; ref: ModelRef },\n feedback: SaveFeedback,\n): Promise<SettingsV1 | undefined> {\n const pick = await env.view.select({\n title: `Resource: ${resourceLabel(ref)}`,\n options: resourceMenuOptions(),\n })\n if (pick === undefined) return undefined\n if (pick === \"remove\") {\n const confirmed = await env.view.confirm({\n title: \"Remove resource\",\n message: `Remove ${resourceLabel(ref)} from ${tier}?`,\n })\n if (confirmed !== true) return undefined\n return applySettings(\n env,\n { tiers: { ...settings.tiers, [tier]: removeResource(settings.tiers[tier], index) } },\n feedback,\n )\n }\n const list =\n pick === \"top\"\n ? moveResourceToTop(settings.tiers[tier], index)\n : moveResource(settings.tiers[tier], index, pick === \"up\" ? -1 : 1)\n return applySettings(env, { tiers: { ...settings.tiers, [tier]: list } }, feedback)\n}\n\ntype PinChoice = { kind: \"clear\" } | { kind: \"pin\"; ref: ModelRef }\n\nfunction pinOptions(settings: SettingsV1): SelectOption<PinChoice>[] {\n const options: SelectOption<PinChoice>[] = [\n { title: \"No pin\", value: { kind: \"clear\" }, description: \"clear the pin\" },\n ]\n for (const tier of TIERS) {\n for (const ref of settings.tiers[tier]) {\n options.push({\n title: `${resourceLabel(ref)} (${tier})`,\n description: \"route every turn to this resource, even in active mode\",\n value: { kind: \"pin\", ref },\n })\n }\n }\n return options\n}\n\nasync function runPinMenu(\n env: AdaptiveEnv,\n settings: SettingsV1,\n feedback: SaveFeedback,\n): Promise<SettingsV1 | undefined> {\n const configured = TIERS.some((tier) => settings.tiers[tier].length > 0)\n if (!configured) {\n await env.view.alert({ title: \"Pin\", message: \"(no configured resources to pin)\" })\n return undefined\n }\n const pick = await env.view.select({ title: \"Pin\", options: pinOptions(settings) })\n if (pick === undefined) return undefined\n return applySettings(env, { pin: pick.kind === \"clear\" ? null : pick.ref }, feedback)\n}\n\nasync function runReceiptsView(env: AdaptiveEnv): Promise<void> {\n const all = await tryApi(env, () => env.api.receipts())\n if (all === undefined) return\n if (all.length === 0) {\n await env.view.alert({\n title: \"Receipts\",\n message: \"No decisions recorded yet.\",\n })\n return\n }\n // Paginated browse: every page is a bounded view, and the page flips are\n // footer actions (not list items) — they close the dialog and the loop\n // reopens it at the new offset. Viewing a detail returns to the same\n // page; Escape (no pick, no action) leaves the browser.\n for (let offset = 0; ;) {\n const page = receiptPage(all.slice(offset))\n const hasNext = offset + RECEIPTS_PAGE_SIZE < all.length\n const result = await env.view.selectPaged({\n title: `Receipts (${String(all.length)}, newest first)`,\n options: page.map((receipt) => ({ title: receiptLine(receipt), value: receipt })),\n actions: [\n ...(offset > 0 ? [{ title: \"Previous page\", value: \"prev\", bind: \"ctrl+b\" }] : []),\n ...(hasNext ? [{ title: \"Next page\", value: \"next\", bind: \"ctrl+f\" }] : []),\n ],\n })\n if (result.action === \"next\") {\n offset += RECEIPTS_PAGE_SIZE\n continue\n }\n if (result.action === \"prev\") {\n offset = Math.max(0, offset - RECEIPTS_PAGE_SIZE)\n continue\n }\n const pick = result.picked\n if (pick === undefined) return\n await env.view.alert({ title: `Receipt #${String(pick.id)}`, message: receiptDetail(pick) })\n }\n}\n\n/** Exported for tests: the receipts page size used by the receipts browser. */\nexport { RECEIPTS_PAGE_SIZE }\n\nexport type {\n MainMenuChoice,\n OperatingMode,\n StatusSnapshot,\n SettingsV1,\n SettingsPatch,\n PatchResult,\n Receipt,\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu.js","sourceRoot":"","sources":["../../../src/ui/flow/menu.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Main-menu and mode-menu view models for `/adaptive` (ADP-033): pure data\n * that the controller feeds into `ui.dialog.select`. Descriptions are dynamic\n * where state makes them more informative (health counters, tier contents).\n */\nimport { OPERATING_MODES } from \"../../core/scheduler/types.js\"\nimport type { OperatingMode } from \"../../core/scheduler/types.js\"\nimport type { TierName } from \"../../core/tier-policy/types.js\"\nimport { TIERS } from \"../../core/tier-policy/types.js\"\nimport type { StatusSnapshot } from \"../../rpc/status.js\"\nimport type { SettingsV1 } from \"../../state/settings/index.js\"\nimport type { SelectOption } from \"./options.js\"\nimport { resourceLabel } from \"./tiers.js\"\n\nexport const MAIN_MENU_ITEMS = [\"status\", \"models\", \"mode\", \"tier\", \"pin\", \"receipts\"] as const\nexport type MainMenuChoice = (typeof MAIN_MENU_ITEMS)[number]\n\n/** \"3 resources · 1 windowed\" — the status line under the menu entry. */\nexport function statusSummary(status: StatusSnapshot | undefined): string {\n if (status === undefined) return \"how every configured model is doing\"\n const total = TIERS.reduce((sum, tier) => sum + status.tiers[tier].length, 0)\n if (total === 0) return \"no models configured yet\"\n const windowed = TIERS.reduce(\n (sum, tier) => sum + status.tiers[tier].filter((entry) => entry.windowed).length,\n 0,\n )\n const windowedNote = windowed === 0 ? \"\" : ` · ${String(windowed)} windowed`\n return `${String(total)} resource${total === 1 ? \"\" : \"s\"}${windowedNote}`\n}\n\n/** \"quick: 1 · standard: 0 · advanced: 2\" — what is configured where. */\nexport function modelsSummary(settings: SettingsV1): string {\n const parts = TIERS.map((tier: TierName) => `${tier}: ${String(settings.tiers[tier].length)}`)\n return parts.join(\" · \")\n}\n\nfunction tierRequestDescription(tierRequest: TierName | null | undefined): string {\n if (tierRequest === undefined) return \"pin a tier for the current session\"\n if (tierRequest === null) return \"not pinned — the routing ladder decides\"\n return `pinned to ${tierRequest} — steers this session`\n}\n\nfunction modeMarker(\n mode: OperatingMode,\n input: { override: OperatingMode | null; effective: OperatingMode },\n): \"override\" | \"current\" | undefined {\n if (mode === input.override) return \"override\"\n if (mode === input.effective) return \"current\"\n return undefined\n}\n\nexport function mainMenuOptions(\n settings: SettingsV1,\n status: StatusSnapshot | undefined,\n tierRequest?: TierName | null,\n effectiveMode: OperatingMode = settings.mode,\n): SelectOption<MainMenuChoice>[] {\n const optionFor = (item: MainMenuChoice): SelectOption<MainMenuChoice> => {\n switch (item) {\n case \"status\":\n return {\n title: \"Status\",\n value: item,\n description: statusSummary(status),\n }\n case \"models\":\n return {\n title: \"Models\",\n value: item,\n description: modelsSummary(settings),\n }\n case \"mode\":\n return {\n title: \"Mode\",\n value: item,\n description: `currently ${effectiveMode}`,\n }\n case \"tier\":\n return {\n title: \"Tier\",\n value: item,\n description: tierRequestDescription(tierRequest),\n }\n case \"pin\":\n return {\n title: \"Pin\",\n value: item,\n description:\n settings.pin === null\n ? \"force one resource\"\n : `pinned to ${resourceLabel(settings.pin)}`,\n }\n case \"receipts\":\n return { title: \"Receipts\", value: item, description: \"why each turn ran the model it ran\" }\n }\n }\n return MAIN_MENU_ITEMS.map(optionFor)\n}\n\nexport function modeOptions(current: OperatingMode): SelectOption<OperatingMode>[] {\n return OPERATING_MODES.map((mode) => ({\n title: mode === current ? `${mode} (current)` : mode,\n value: mode,\n description: modeDescription(mode),\n }))\n}\n\n/** What one mode does — shared by the global and the per-session menus. */\nfunction modeDescription(mode: OperatingMode): string {\n switch (mode) {\n case \"off\":\n return \"Adaptive does nothing\"\n case \"observe\":\n return \"Record decisions without applying them\"\n case \"active\":\n return \"Apply routing and recovery decisions\"\n }\n}\n\n/** One per-session mode-menu pick: follow the global mode, or set an override. */\nexport type SessionModeChoice = { kind: \"global\" } | { kind: \"mode\"; mode: OperatingMode }\n\n/** Which mode the user steers inside a session: the global default or the session override. */\nexport type ModeTarget = \"global\" | \"session\"\n\n/**\n * The first select of the in-session mode flow (ADP-054): ask which mode to\n * steer before showing any mode list — the global default or this session's\n * override. Each row names the current value so the user can tell at a\n * glance which side needs changing.\n */\nexport function modeTargetOptions(\n global: OperatingMode,\n effective: OperatingMode,\n): SelectOption<ModeTarget>[] {\n return [\n {\n title: \"Global mode…\",\n description: `applies to every session without an override (now ${global})`,\n value: \"global\",\n },\n {\n title: \"This session…\",\n description: `override for this session only (now ${effective})`,\n value: \"session\",\n },\n ]\n}\n\n/**\n * The per-session mode menu (ADP-054): first \"Follow global\" (clearing the\n * override), then every mode. An existing override is marked \"(override)\" —\n * distinct from the effective mode's \"(current)\" — so the user can tell a\n * set override apart from a session merely inheriting the global mode.\n */\nexport function sessionModeOptions(input: {\n global: OperatingMode\n override: OperatingMode | null\n effective: OperatingMode\n}): SelectOption<SessionModeChoice>[] {\n const options: SelectOption<SessionModeChoice>[] = [\n {\n title: `Follow global (${input.global})`,\n description: \"clear this session's override\",\n value: { kind: \"global\" },\n },\n ]\n for (const mode of OPERATING_MODES) {\n const marker = modeMarker(mode, input)\n options.push({\n title: marker === undefined ? mode : `${mode} (${marker})`,\n value: { kind: \"mode\", mode },\n description: modeDescription(mode),\n })\n }\n return options\n}\n\n/** What one tier is for — shared by the tier editor and the per-session menu. */\nfunction tierDescription(tier: TierName): string {\n switch (tier) {\n case \"quick\":\n return \"routine edits, mechanical steps\"\n case \"standard\":\n return \"the default\"\n case \"advanced\":\n return \"multi-step design, broad refactors\"\n }\n}\n\n/** One per-session tier-menu pick: follow the routing ladder, or pin a tier. */\nexport type SessionTierChoice = { kind: \"ladder\" } | { kind: \"tier\"; tier: TierName }\n\n/**\n * The per-session tier menu (ADP-055): first \"Follow routing ladder\" (clearing\n * the request), then every tier. A pinned request is marked \"(pinned)\" so the\n * user can tell the sticky request apart from the ordinary ladder.\n */\nexport function sessionTierOptions(input: {\n pinned: TierName | null\n}): SelectOption<SessionTierChoice>[] {\n const options: SelectOption<SessionTierChoice>[] = [\n {\n title: \"Follow routing ladder\",\n description: \"clear this session's tier request\",\n value: { kind: \"ladder\" },\n },\n ]\n for (const tier of TIERS) {\n options.push({\n title: tier === input.pinned ? `${tier} (pinned)` : tier,\n value: { kind: \"tier\", tier },\n description: tierDescription(tier),\n })\n }\n return options\n}\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared option shapes for the dialog-driven /adaptive flow (TUI and GUI). These mirror the subset of
|
|
3
|
+
* the host `ui.dialog.select` contract the Adaptive flows use; `tui.tsx` and `gui.tsx` adapt
|
|
4
|
+
* them to the real plugin types (structural superset).
|
|
5
|
+
*/
|
|
6
|
+
export interface SelectOption<Value> {
|
|
7
|
+
readonly title: string;
|
|
8
|
+
readonly value: Value;
|
|
9
|
+
readonly description?: string;
|
|
10
|
+
}
|
|
11
|
+
/** Friendly one-line rendering of an API failure for toasts. */
|
|
12
|
+
export declare function errorText(error: unknown): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.js","sourceRoot":"","sources":["../../../src/ui/flow/options.ts"],"names":[],"mappings":"AAWA,gEAAgE;AAChE,MAAM,UAAU,SAAS,CAAC,KAAc;IACtC,IAAI,KAAK,YAAY,KAAK;QAAE,OAAO,KAAK,CAAC,OAAO,CAAA;IAChD,0EAA0E;IAC1E,6EAA6E;IAC7E,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAChD,MAAM,OAAO,GAAI,KAA+B,CAAC,OAAO,CAAA;QACxD,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,OAAO,CAAA;QACrE,yEAAyE;QACzE,oEAAoE;QACpE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,KAAc,EAAE,EAAE,CACjD,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAClD,CAAA;IACH,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAA;AACtB,CAAC","sourcesContent":["/**\n * Shared option shapes for the dialog-driven /adaptive flow (TUI and GUI). These mirror the subset of\n * the host `ui.dialog.select` contract the Adaptive flows use; `tui.tsx` and `gui.tsx` adapt\n * them to the real plugin types (structural superset).\n */\nexport interface SelectOption<Value> {\n readonly title: string\n readonly value: Value\n readonly description?: string\n}\n\n/** Friendly one-line rendering of an API failure for toasts. */\nexport function errorText(error: unknown): string {\n if (error instanceof Error) return error.message\n // RPC failures surface as plain objects ({type, message}); String() would\n // render \"[object Object]\" and throw away the only diagnostic the user gets.\n if (typeof error === \"object\" && error !== null) {\n const message = (error as { message?: unknown }).message\n if (typeof message === \"string\" && message.length > 0) return message\n // JSON.stringify throws on BigInt values, which host payloads can carry;\n // stringify them as strings instead of losing the whole diagnostic.\n return JSON.stringify(error, (_, value: unknown) =>\n typeof value === \"bigint\" ? String(value) : value,\n )\n }\n return String(error)\n}\n"]}
|
|
@@ -13,7 +13,7 @@ export declare function receiptDetail(receipt: Receipt, now?: number): string;
|
|
|
13
13
|
export declare function relativeTime(at: number, now: number): string;
|
|
14
14
|
/**
|
|
15
15
|
* Plain-language explanation for a receipt, keyed on the finite reason
|
|
16
|
-
* vocabulary
|
|
16
|
+
* vocabulary (the maps are total over it).
|
|
17
17
|
*/
|
|
18
18
|
export declare function whatHappened(receipt: Receipt): string;
|
|
19
19
|
/** Receipts shown per dialog page (bounded view). */
|
|
@@ -61,19 +61,17 @@ export function relativeTime(at, now) {
|
|
|
61
61
|
}
|
|
62
62
|
/**
|
|
63
63
|
* Plain-language explanation for a receipt, keyed on the finite reason
|
|
64
|
-
* vocabulary
|
|
64
|
+
* vocabulary (the maps are total over it).
|
|
65
65
|
*/
|
|
66
66
|
export function whatHappened(receipt) {
|
|
67
67
|
const decision = receipt.decision;
|
|
68
68
|
switch (decision.kind) {
|
|
69
69
|
case "apply":
|
|
70
|
-
return
|
|
71
|
-
`Adaptive routed this turn to ${resourceLabel(decision.model)}.`);
|
|
70
|
+
return applyExplanation[decision.reason];
|
|
72
71
|
case "defer":
|
|
73
|
-
return deferExplanation[decision.reason]
|
|
72
|
+
return deferExplanation[decision.reason];
|
|
74
73
|
case "park":
|
|
75
|
-
return
|
|
76
|
-
"Adaptive could not route this turn and left it to OpenCode.");
|
|
74
|
+
return parkExplanation[decision.reason];
|
|
77
75
|
case "escalate":
|
|
78
76
|
return `This turn needed stronger capability, so Adaptive moved it to ${resourceLabel(decision.target)}.`;
|
|
79
77
|
case "observe-only":
|
|
@@ -89,7 +87,6 @@ const applyExplanation = {
|
|
|
89
87
|
};
|
|
90
88
|
const deferExplanation = {
|
|
91
89
|
"routing-defer": "No routing rule applied to this turn, so Adaptive left the choice to OpenCode.",
|
|
92
|
-
"tier-not-configured": "The tier that would handle this turn has no models configured, so Adaptive left the choice to OpenCode.",
|
|
93
90
|
"adaptive-off": "Adaptive is switched off, so every turn runs on OpenCode's own choice.",
|
|
94
91
|
"pin-unverifiable": "The pinned resource could not be verified as available, so Adaptive left the choice to OpenCode.",
|
|
95
92
|
"pin-incompatible": "The pinned resource does not fit this turn's requirements, so Adaptive left the choice to OpenCode.",
|