@nextclaw/ui 0.11.14 → 0.11.16
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/CHANGELOG.md +17 -0
- package/dist/assets/{ChannelsList-CvK4qHfg.js → ChannelsList-UKA-5t02.js} +1 -1
- package/dist/assets/ChatPage-xdBp-ddG.js +37 -0
- package/dist/assets/{DocBrowser-BFmW6e-4.js → DocBrowser-DBUIWJer.js} +1 -1
- package/dist/assets/{LogoBadge-DZL-zQTr.js → LogoBadge-CsSBHZeV.js} +1 -1
- package/dist/assets/{MarketplacePage-B__MZRrD.js → MarketplacePage-BSH836_G.js} +1 -1
- package/dist/assets/{McpMarketplacePage-C_VKm1uq.js → McpMarketplacePage-B9_kPnnM.js} +1 -1
- package/dist/assets/{ModelConfig-CqJubuwU.js → ModelConfig-DbEKVVg4.js} +1 -1
- package/dist/assets/{ProvidersList-BoSsFBk5.js → ProvidersList-Ck5PgTF2.js} +1 -1
- package/dist/assets/{RemoteAccessPage-S1ChRWMX.js → RemoteAccessPage-4JED9IcK.js} +1 -1
- package/dist/assets/{RuntimeConfig-WnFUsayT.js → RuntimeConfig-CB04ug9v.js} +1 -1
- package/dist/assets/{SearchConfig-D9V07oqj.js → SearchConfig-DmuvL9Pn.js} +1 -1
- package/dist/assets/{SecretsConfig-Ci8sEzaV.js → SecretsConfig-Dw2sRiSs.js} +1 -1
- package/dist/assets/{SessionsConfig-5Nznhx9P.js → SessionsConfig-c-Z9X3xH.js} +1 -1
- package/dist/assets/{chat-session-display-D0ZcEkUq.js → chat-session-display-z9RvX-D3.js} +1 -1
- package/dist/assets/{index-BvCYcN48.js → index-DqzLj8Sw.js} +3 -3
- package/dist/assets/{label-AurG3ZpO.js → label-DPGDZvhm.js} +1 -1
- package/dist/assets/{page-layout-Q2hHkfJy.js → page-layout-DMA_ZiHj.js} +1 -1
- package/dist/assets/{popover-BKInm43u.js → popover-BBLXwfva.js} +1 -1
- package/dist/assets/{security-config-BbPGNJAB.js → security-config-FYNEE2eR.js} +1 -1
- package/dist/assets/{skeleton-CuKw6-Ww.js → skeleton-DLM_39_P.js} +1 -1
- package/dist/assets/{status-dot-DLk8UxLB.js → status-dot-BOzEprxw.js} +1 -1
- package/dist/assets/{switch-BxMSKsQS.js → switch-rE_Ew8fl.js} +1 -1
- package/dist/assets/{tabs-custom-B6gK-RY6.js → tabs-custom-BkfMUTHE.js} +1 -1
- package/dist/assets/{useConfirmDialog-Dth62a0a.js → useConfirmDialog-0owrqZcT.js} +1 -1
- package/dist/index.html +1 -1
- package/package.json +5 -5
- package/src/api/ncp-session-query-cache.test.ts +64 -0
- package/src/api/ncp-session-query-cache.ts +72 -2
- package/src/api/types.ts +1 -0
- package/src/components/chat/adapters/chat-message.adapter.test.ts +40 -0
- package/src/components/chat/adapters/chat-message.adapter.ts +11 -0
- package/src/components/chat/adapters/chat-message.subagent-tool-card.ts +125 -0
- package/src/components/chat/ncp/NcpChatPage.tsx +19 -35
- package/src/components/config/README.md +1 -1
- package/src/hooks/use-realtime-query-bridge.ts +5 -1
- package/dist/assets/ChatPage-Co3GqIVP.js +0 -37
- package/src/components/chat/ncp/ncp-chat-realtime-reload.test.ts +0 -44
- package/src/components/chat/ncp/ncp-chat-realtime-reload.ts +0 -20
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as s,j as o}from"./vendor-MCpnpiKt.js";import{c as t}from"./index-
|
|
1
|
+
import{r as s,j as o}from"./vendor-MCpnpiKt.js";import{c as t}from"./index-DqzLj8Sw.js";const l=s.forwardRef(({className:e,...a},r)=>o.jsx("label",{ref:r,className:t("text-sm font-medium leading-none text-gray-700 peer-disabled:cursor-not-allowed peer-disabled:opacity-70",e),...a}));l.displayName="Label";export{l as L};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{j as e}from"./vendor-MCpnpiKt.js";import{c as l}from"./index-
|
|
1
|
+
import{j as e}from"./vendor-MCpnpiKt.js";import{c as l}from"./index-DqzLj8Sw.js";function x({children:t,fullHeight:s=!1,className:a}){return e.jsx("div",{className:l("animate-fade-in",s?"h-[calc(100vh-80px)] w-full flex flex-col":"pb-16",a),children:t})}function c({title:t,description:s,actions:a,className:r}){return e.jsxs("div",{className:l("flex items-center justify-between mb-6 shrink-0",r),children:[e.jsxs("div",{children:[e.jsx("h2",{className:"text-xl font-semibold text-gray-900",children:t}),s&&e.jsx("p",{className:"text-sm text-gray-500 mt-1",children:s})]}),a&&e.jsx("div",{className:"flex items-center gap-2 shrink-0",children:a})]})}export{x as P,c as a};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as l,aJ as L,j as c,aK as z,aL as G,aM as A,aN as O,aO as P,aP as _,aQ as w,aR as b,aS as H,aT as K,aU as U,aV as V,aW as W,aX as Z,aY as J,aZ as Q,a_ as X,a$ as Y}from"./vendor-MCpnpiKt.js";import{c as q}from"./index-
|
|
1
|
+
import{r as l,aJ as L,j as c,aK as z,aL as G,aM as A,aN as O,aO as P,aP as _,aQ as w,aR as b,aS as H,aT as K,aU as U,aV as V,aW as W,aX as Z,aY as J,aZ as Q,a_ as X,a$ as Y}from"./vendor-MCpnpiKt.js";import{c as q}from"./index-DqzLj8Sw.js";var m="Popover",[E]=K(m,[w]),g=w(),[B,d]=E(m),j=e=>{const{__scopePopover:a,children:t,open:n,defaultOpen:o,onOpenChange:r,modal:s=!1}=e,i=g(a),p=l.useRef(null),[u,h]=l.useState(!1),[C,f]=L({prop:n,defaultProp:o??!1,onChange:r,caller:m});return c.jsx(z,{...i,children:c.jsx(B,{scope:a,contentId:G(),triggerRef:p,open:C,onOpenChange:f,onOpenToggle:l.useCallback(()=>f(x=>!x),[f]),hasCustomAnchor:u,onCustomAnchorAdd:l.useCallback(()=>h(!0),[]),onCustomAnchorRemove:l.useCallback(()=>h(!1),[]),modal:s,children:t})})};j.displayName=m;var N="PopoverAnchor",y=l.forwardRef((e,a)=>{const{__scopePopover:t,...n}=e,o=d(N,t),r=g(t),{onCustomAnchorAdd:s,onCustomAnchorRemove:i}=o;return l.useEffect(()=>(s(),()=>i()),[s,i]),c.jsx(_,{...r,...n,ref:a})});y.displayName=N;var F="PopoverTrigger",S=l.forwardRef((e,a)=>{const{__scopePopover:t,...n}=e,o=d(F,t),r=g(t),s=A(a,o.triggerRef),i=c.jsx(O.button,{type:"button","aria-haspopup":"dialog","aria-expanded":o.open,"aria-controls":o.contentId,"data-state":I(o.open),...n,ref:s,onClick:P(e.onClick,o.onOpenToggle)});return o.hasCustomAnchor?i:c.jsx(_,{asChild:!0,...r,children:i})});S.displayName=F;var R="PopoverPortal",[ee,oe]=E(R,{forceMount:void 0}),M=e=>{const{__scopePopover:a,forceMount:t,children:n,container:o}=e,r=d(R,a);return c.jsx(ee,{scope:a,forceMount:t,children:c.jsx(b,{present:t||r.open,children:c.jsx(H,{asChild:!0,container:o,children:n})})})};M.displayName=R;var v="PopoverContent",T=l.forwardRef((e,a)=>{const t=oe(v,e.__scopePopover),{forceMount:n=t.forceMount,...o}=e,r=d(v,e.__scopePopover);return c.jsx(b,{present:n||r.open,children:r.modal?c.jsx(re,{...o,ref:a}):c.jsx(ae,{...o,ref:a})})});T.displayName=v;var te=W("PopoverContent.RemoveScroll"),re=l.forwardRef((e,a)=>{const t=d(v,e.__scopePopover),n=l.useRef(null),o=A(a,n),r=l.useRef(!1);return l.useEffect(()=>{const s=n.current;if(s)return U(s)},[]),c.jsx(V,{as:te,allowPinchZoom:!0,children:c.jsx(D,{...e,ref:o,trapFocus:t.open,disableOutsidePointerEvents:!0,onCloseAutoFocus:P(e.onCloseAutoFocus,s=>{var i;s.preventDefault(),r.current||(i=t.triggerRef.current)==null||i.focus()}),onPointerDownOutside:P(e.onPointerDownOutside,s=>{const i=s.detail.originalEvent,p=i.button===0&&i.ctrlKey===!0,u=i.button===2||p;r.current=u},{checkForDefaultPrevented:!1}),onFocusOutside:P(e.onFocusOutside,s=>s.preventDefault(),{checkForDefaultPrevented:!1})})})}),ae=l.forwardRef((e,a)=>{const t=d(v,e.__scopePopover),n=l.useRef(!1),o=l.useRef(!1);return c.jsx(D,{...e,ref:a,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:r=>{var s,i;(s=e.onCloseAutoFocus)==null||s.call(e,r),r.defaultPrevented||(n.current||(i=t.triggerRef.current)==null||i.focus(),r.preventDefault()),n.current=!1,o.current=!1},onInteractOutside:r=>{var p,u;(p=e.onInteractOutside)==null||p.call(e,r),r.defaultPrevented||(n.current=!0,r.detail.originalEvent.type==="pointerdown"&&(o.current=!0));const s=r.target;((u=t.triggerRef.current)==null?void 0:u.contains(s))&&r.preventDefault(),r.detail.originalEvent.type==="focusin"&&o.current&&r.preventDefault()}})}),D=l.forwardRef((e,a)=>{const{__scopePopover:t,trapFocus:n,onOpenAutoFocus:o,onCloseAutoFocus:r,disableOutsidePointerEvents:s,onEscapeKeyDown:i,onPointerDownOutside:p,onFocusOutside:u,onInteractOutside:h,...C}=e,f=d(v,t),x=g(t);return Z(),c.jsx(J,{asChild:!0,loop:!0,trapped:n,onMountAutoFocus:o,onUnmountAutoFocus:r,children:c.jsx(Q,{asChild:!0,disableOutsidePointerEvents:s,onInteractOutside:h,onEscapeKeyDown:i,onPointerDownOutside:p,onFocusOutside:u,onDismiss:()=>f.onOpenChange(!1),children:c.jsx(X,{"data-state":I(f.open),role:"dialog",id:f.contentId,...x,...C,ref:a,style:{...C.style,"--radix-popover-content-transform-origin":"var(--radix-popper-transform-origin)","--radix-popover-content-available-width":"var(--radix-popper-available-width)","--radix-popover-content-available-height":"var(--radix-popper-available-height)","--radix-popover-trigger-width":"var(--radix-popper-anchor-width)","--radix-popover-trigger-height":"var(--radix-popper-anchor-height)"}})})})}),k="PopoverClose",ne=l.forwardRef((e,a)=>{const{__scopePopover:t,...n}=e,o=d(k,t);return c.jsx(O.button,{type:"button",...n,ref:a,onClick:P(e.onClick,()=>o.onOpenChange(!1))})});ne.displayName=k;var se="PopoverArrow",ce=l.forwardRef((e,a)=>{const{__scopePopover:t,...n}=e,o=g(t);return c.jsx(Y,{...o,...n,ref:a})});ce.displayName=se;function I(e){return e?"open":"closed"}var ie=j,ve=y,le=S,pe=M,$=T;const Pe=ie,ge=le,ue=l.forwardRef(({className:e,sideOffset:a=8,align:t="start",...n},o)=>c.jsx(pe,{children:c.jsx($,{ref:o,sideOffset:a,align:t,className:q("z-[var(--z-popover,50)] w-72 overflow-hidden rounded-2xl border border-gray-200/50 bg-white p-4 shadow-lg animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",e),...n})}));ue.displayName=$.displayName;export{ve as A,$ as C,pe as P,ie as R,le as T,Pe as a,ge as b,ue as c};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as c,j as e,e as M}from"./vendor-MCpnpiKt.js";import{a3 as O,a4 as R,a5 as I,a6 as B,a7 as _,C as u,a0 as h,a1 as x,t as s,a2 as m,E as p,B as j,I as l}from"./index-
|
|
1
|
+
import{r as c,j as e,e as M}from"./vendor-MCpnpiKt.js";import{a3 as O,a4 as R,a5 as I,a6 as B,a7 as _,C as u,a0 as h,a1 as x,t as s,a2 as m,E as p,B as j,I as l}from"./index-DqzLj8Sw.js";import{L as o}from"./label-DPGDZvhm.js";import{S as G}from"./switch-rE_Ew8fl.js";import{P as V,a as W}from"./page-layout-DMA_ZiHj.js";const q=8;function A(r){return r.trim().length>=q}function L(r,n){return r!==n?(M.error(s("authPasswordMismatch")),!1):!0}function z(){const r=O(),n=R(),S=I(),g=B(),y=_(),[f,U]=c.useState(""),[i,N]=c.useState(""),[w,b]=c.useState(""),[d,v]=c.useState(""),[P,C]=c.useState(""),t=r.data,E=f.trim().length>0&&A(i)&&i===w&&!n.isPending,D=A(d)&&d===P&&!g.isPending,T=async()=>{if(L(i,w))try{await n.mutateAsync({username:f.trim(),password:i}),N(""),b("")}catch{}},F=async()=>{if(L(d,P))try{await g.mutateAsync({password:d}),v(""),C("")}catch{}},H=async a=>{try{await S.mutateAsync({enabled:a})}catch{}},k=async()=>{try{await y.mutateAsync()}catch{}};return r.isLoading&&!t?e.jsxs(u,{children:[e.jsxs(h,{children:[e.jsx(x,{children:s("authSecurityTitle")}),e.jsx(m,{children:s("authSecurityDescription")})]}),e.jsx(p,{className:"text-sm text-gray-500",children:s("loading")})]}):r.isError||!t?e.jsxs(u,{children:[e.jsxs(h,{children:[e.jsx(x,{children:s("authSecurityTitle")}),e.jsx(m,{children:s("authSecurityDescription")})]}),e.jsxs(p,{className:"space-y-4",children:[e.jsx("p",{className:"text-sm text-gray-500",children:s("authStatusLoadFailed")}),e.jsx(j,{variant:"outline",onClick:()=>{r.refetch()},children:s("authRetryStatus")})]})]}):t.configured?e.jsxs(u,{children:[e.jsxs(h,{children:[e.jsx(x,{children:s("authSecurityTitle")}),e.jsx(m,{children:s("authSecurityDescription")})]}),e.jsxs(p,{className:"space-y-6",children:[e.jsxs("div",{className:"rounded-xl border border-gray-200 p-4",children:[e.jsxs("div",{className:"flex flex-col gap-4 md:flex-row md:items-start md:justify-between",children:[e.jsxs("div",{className:"space-y-1",children:[e.jsx("p",{className:"text-sm font-medium text-gray-900",children:s("authStatusLabel")}),e.jsx("p",{className:"text-sm text-gray-600",children:s("authStatusConfiguredUser").replace("{username}",t.username??"")}),e.jsx("p",{className:"text-xs text-gray-500",children:s("authUsernameFixedHelp")})]}),e.jsx("span",{className:"inline-flex items-center rounded-full bg-gray-100 px-3 py-1 text-xs font-medium text-gray-700",children:t.enabled?s("enabled"):s("disabled")})]}),e.jsxs("div",{className:"mt-4 flex flex-col gap-4 border-t border-gray-200 pt-4 md:flex-row md:items-center md:justify-between",children:[e.jsxs("div",{className:"space-y-1",children:[e.jsx("p",{className:"text-sm font-medium text-gray-900",children:s("authEnableLabel")}),e.jsx("p",{className:"text-xs text-gray-500",children:t.enabled?s("authEnableOnHelp"):s("authEnableOffHelp")})]}),e.jsx(G,{checked:t.enabled,disabled:S.isPending,onCheckedChange:a=>{H(a)}})]})]}),e.jsxs("div",{className:"rounded-xl border border-gray-200 p-4 space-y-4",children:[e.jsxs("div",{className:"space-y-1",children:[e.jsx("p",{className:"text-sm font-medium text-gray-900",children:s("authPasswordSectionTitle")}),e.jsx("p",{className:"text-xs text-gray-500",children:s("authPasswordSectionDescription")})]}),e.jsxs("div",{className:"grid gap-4 md:grid-cols-2",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(o,{htmlFor:"auth-password-next",children:s("authPassword")}),e.jsx(l,{id:"auth-password-next",type:"password",value:d,onChange:a=>v(a.target.value),placeholder:s("authPasswordPlaceholder")})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(o,{htmlFor:"auth-password-confirm",children:s("authConfirmPassword")}),e.jsx(l,{id:"auth-password-confirm",type:"password",value:P,onChange:a=>C(a.target.value),placeholder:s("authConfirmPasswordPlaceholder")})]})]}),e.jsxs("div",{className:"flex flex-wrap items-center gap-3",children:[e.jsx(j,{type:"button",disabled:!D,onClick:()=>void F(),children:g.isPending?s("authPasswordUpdating"):s("authPasswordAction")}),t.enabled&&t.authenticated?e.jsx(j,{type:"button",variant:"outline",disabled:y.isPending,onClick:()=>void k(),children:y.isPending?s("authLoggingOut"):s("authLogoutAction")}):null]}),e.jsx("p",{className:"text-xs text-gray-500",children:s("authSessionMemoryNotice")})]})]})]}):e.jsxs(u,{children:[e.jsxs(h,{children:[e.jsx(x,{children:s("authSecurityTitle")}),e.jsx(m,{children:s("authSecurityDescription")})]}),e.jsxs(p,{className:"space-y-5",children:[e.jsxs("div",{className:"rounded-xl border border-dashed border-gray-200 bg-gray-50/70 p-4",children:[e.jsx("p",{className:"text-sm font-medium text-gray-900",children:s("authSetupTitle")}),e.jsx("p",{className:"mt-1 text-sm text-gray-500",children:s("authSetupDescription")})]}),e.jsxs("div",{className:"grid gap-4 md:grid-cols-2",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsx(o,{htmlFor:"auth-setup-username",children:s("authUsername")}),e.jsx(l,{id:"auth-setup-username",value:f,onChange:a=>U(a.target.value),placeholder:s("authUsernamePlaceholder")})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(o,{htmlFor:"auth-setup-password",children:s("authPassword")}),e.jsx(l,{id:"auth-setup-password",type:"password",value:i,onChange:a=>N(a.target.value),placeholder:s("authPasswordPlaceholder")})]})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx(o,{htmlFor:"auth-setup-confirm",children:s("authConfirmPassword")}),e.jsx(l,{id:"auth-setup-confirm",type:"password",value:w,onChange:a=>b(a.target.value),placeholder:s("authConfirmPasswordPlaceholder")}),e.jsx("p",{className:"text-xs text-gray-500",children:s("authPasswordMinLengthHint")})]}),e.jsx(j,{type:"button",disabled:!E,onClick:()=>void T(),children:n.isPending?s("authSettingUp"):s("authSetupAction")})]})]})}function Z(){return e.jsxs(V,{className:"space-y-6",children:[e.jsx(W,{title:s("authSecurityTitle"),description:s("authSecurityDescription")}),e.jsx(z,{})]})}export{Z as SecurityConfig};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{j as t}from"./vendor-MCpnpiKt.js";import{c as o}from"./index-
|
|
1
|
+
import{j as t}from"./vendor-MCpnpiKt.js";import{c as o}from"./index-DqzLj8Sw.js";function m({className:e,...s}){return t.jsx("div",{className:o("animate-pulse rounded-md bg-slate-200",e),...s})}export{m as S};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{j as e}from"./vendor-MCpnpiKt.js";import{c as a}from"./index-
|
|
1
|
+
import{j as e}from"./vendor-MCpnpiKt.js";import{c as a}from"./index-DqzLj8Sw.js";const n={active:{dot:"bg-emerald-500",text:"text-emerald-600",bg:"bg-emerald-50"},ready:{dot:"bg-emerald-500",text:"text-emerald-600",bg:"bg-emerald-50"},inactive:{dot:"bg-gray-300",text:"text-gray-400",bg:"bg-gray-100/80"},setup:{dot:"bg-gray-300",text:"text-gray-400",bg:"bg-gray-100/80"},warning:{dot:"bg-amber-400",text:"text-amber-600",bg:"bg-amber-50"}};function m({status:r,label:s,className:g}){const t=n[r];return e.jsxs("div",{className:a("inline-flex shrink-0 items-center gap-1.5 whitespace-nowrap rounded-full px-2 py-0.5",t.bg,g),children:[e.jsx("span",{className:a("h-1.5 w-1.5 rounded-full",t.dot)}),e.jsx("span",{className:a("text-[11px] font-medium",t.text),children:s})]})}export{m as S};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as e,j as i}from"./vendor-MCpnpiKt.js";import{c as t}from"./index-
|
|
1
|
+
import{r as e,j as i}from"./vendor-MCpnpiKt.js";import{c as t}from"./index-DqzLj8Sw.js";const l=e.forwardRef(({className:o,checked:r=!1,onCheckedChange:s,...a},n)=>i.jsx("button",{type:"button",role:"switch","aria-checked":r,ref:n,className:t("peer inline-flex h-[22px] w-10 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors duration-fast focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-white disabled:cursor-not-allowed disabled:opacity-50",r?"bg-primary":"bg-gray-200/80 hover:bg-gray-300/80",o),onClick:()=>s==null?void 0:s(!r),...a,children:i.jsx("span",{className:t("pointer-events-none block h-5 w-5 rounded-full bg-white shadow-md ring-0 transition-transform duration-fast",r?"translate-x-5":"translate-x-0")})}));l.displayName="Switch";export{l as S};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{j as e}from"./vendor-MCpnpiKt.js";import{an as m,c as a}from"./index-
|
|
1
|
+
import{j as e}from"./vendor-MCpnpiKt.js";import{an as m,c as a}from"./index-DqzLj8Sw.js";function c({tabs:s,activeTab:i,onChange:o,className:n}){return e.jsx("div",{className:a("flex items-center gap-6 border-b border-gray-200/60 mb-6",n),children:s.map(t=>{const r=i===t.id;return e.jsxs("button",{onClick:()=>o(t.id),className:a("relative pb-3 text-[14px] font-medium transition-all duration-fast flex items-center gap-1.5",r?"text-gray-900":"text-gray-600 hover:text-gray-900"),children:[t.label,t.count!==void 0&&e.jsx("span",{className:a("text-[11px] font-medium","text-gray-500"),children:m(t.count)}),r&&e.jsx("div",{className:"absolute bottom-0 left-0 right-0 h-[2px] bg-primary rounded-full"})]},t.id)})})}export{c as T};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{j as a,r as t}from"./vendor-MCpnpiKt.js";import{ah as C,ai as h,aj as p,ak as x,al as g,am as j,B as d,t as r}from"./index-
|
|
1
|
+
import{j as a,r as t}from"./vendor-MCpnpiKt.js";import{ah as C,ai as h,aj as p,ak as x,al as g,am as j,B as d,t as r}from"./index-DqzLj8Sw.js";const D=({open:l,onOpenChange:i,title:c,description:o,confirmLabel:s=r("confirm"),cancelLabel:e=r("cancel"),variant:n="default",onConfirm:u,onCancel:f})=>{const m=()=>{u(),i(!1)},v=()=>{f(),i(!1)};return a.jsx(C,{open:l,onOpenChange:i,children:a.jsxs(h,{className:"[&>:last-child]:hidden",onCloseAutoFocus:b=>b.preventDefault(),children:[a.jsxs(p,{children:[a.jsx(x,{children:c}),o?a.jsx(g,{children:o}):null]}),a.jsxs(j,{className:"gap-2 sm:gap-0",children:[a.jsx(d,{type:"button",variant:"outline",onClick:v,children:e}),a.jsx(d,{type:"button",variant:n==="destructive"?"destructive":"default",onClick:m,children:s})]})]})})},L={open:!1,title:"",description:"",confirmLabel:r("confirm"),cancelLabel:r("cancel"),variant:"default",resolve:null};function y(){const[l,i]=t.useState(L),c=t.useCallback(e=>new Promise(n=>{i({open:!0,title:e.title,description:e.description??"",confirmLabel:e.confirmLabel??r("confirm"),cancelLabel:e.cancelLabel??r("cancel"),variant:e.variant??"default",resolve:u=>{n(u),i(f=>({...f,open:!1,resolve:null}))}})}),[]),o=t.useCallback(e=>{i(n=>(!e&&n.resolve&&n.resolve(!1),{...n,open:e,resolve:e?n.resolve:null}))},[]),s=t.useCallback(()=>a.jsx(D,{open:l.open,onOpenChange:o,title:l.title,description:l.description||void 0,confirmLabel:l.confirmLabel,cancelLabel:l.cancelLabel,variant:l.variant,onConfirm:()=>{var e;return(e=l.resolve)==null?void 0:e.call(l,!0)},onCancel:()=>{var e;return(e=l.resolve)==null?void 0:e.call(l,!1)}}),[l,o]);return{confirm:c,ConfirmDialog:s}}export{y as u};
|
package/dist/index.html
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
8
8
|
<title>NextClaw</title>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-DqzLj8Sw.js"></script>
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/vendor-MCpnpiKt.js">
|
|
11
11
|
<link rel="stylesheet" crossorigin href="/assets/index-CfVmBgkf.css">
|
|
12
12
|
</head>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextclaw/ui",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.16",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
"zod": "^3.23.8",
|
|
30
30
|
"zustand": "^5.0.2",
|
|
31
31
|
"@nextclaw/agent-chat": "0.1.4",
|
|
32
|
-
"@nextclaw/
|
|
33
|
-
"@nextclaw/ncp": "0.
|
|
34
|
-
"@nextclaw/ncp-react": "0.4.
|
|
35
|
-
"@nextclaw/
|
|
32
|
+
"@nextclaw/ncp": "0.4.2",
|
|
33
|
+
"@nextclaw/ncp-http-agent-client": "0.3.6",
|
|
34
|
+
"@nextclaw/ncp-react": "0.4.7",
|
|
35
|
+
"@nextclaw/agent-chat-ui": "0.2.14"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@testing-library/react": "^16.3.0",
|
|
@@ -3,6 +3,7 @@ import { describe, expect, it } from 'vitest';
|
|
|
3
3
|
import {
|
|
4
4
|
applyNcpSessionRealtimeEvent,
|
|
5
5
|
deleteNcpSessionSummaryList,
|
|
6
|
+
updateNcpSessionRunStatusList,
|
|
6
7
|
upsertNcpSessionSummaryList
|
|
7
8
|
} from '@/api/ncp-session-query-cache';
|
|
8
9
|
import type { NcpSessionsListView } from '@/api/types';
|
|
@@ -47,6 +48,45 @@ describe('ncp-session-query-cache', () => {
|
|
|
47
48
|
});
|
|
48
49
|
});
|
|
49
50
|
|
|
51
|
+
it('ignores stale summaries that would move a session back to an older running state', () => {
|
|
52
|
+
const updated = upsertNcpSessionSummaryList(createSessionsList(), {
|
|
53
|
+
sessionId: 'session-1',
|
|
54
|
+
messageCount: 9,
|
|
55
|
+
updatedAt: '2026-03-29T09:00:00.000Z',
|
|
56
|
+
status: 'running',
|
|
57
|
+
metadata: { label: 'Stale running' }
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
expect(updated?.sessions[0]).toMatchObject({
|
|
61
|
+
sessionId: 'session-1',
|
|
62
|
+
messageCount: 1,
|
|
63
|
+
status: 'idle'
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('prefers idle over running when summaries arrive with the same timestamp', () => {
|
|
68
|
+
const current = createSessionsList();
|
|
69
|
+
current.sessions[0] = {
|
|
70
|
+
...current.sessions[0],
|
|
71
|
+
updatedAt: '2026-03-29T10:00:00.000Z',
|
|
72
|
+
status: 'idle'
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const updated = upsertNcpSessionSummaryList(current, {
|
|
76
|
+
sessionId: 'session-1',
|
|
77
|
+
messageCount: 5,
|
|
78
|
+
updatedAt: '2026-03-29T10:00:00.000Z',
|
|
79
|
+
status: 'running',
|
|
80
|
+
metadata: {}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
expect(updated?.sessions[0]).toMatchObject({
|
|
84
|
+
sessionId: 'session-1',
|
|
85
|
+
messageCount: 1,
|
|
86
|
+
status: 'idle'
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
50
90
|
it('deletes summaries without mutating unrelated entries', () => {
|
|
51
91
|
const updated = deleteNcpSessionSummaryList(createSessionsList(), 'session-1');
|
|
52
92
|
|
|
@@ -54,10 +94,34 @@ describe('ncp-session-query-cache', () => {
|
|
|
54
94
|
expect(updated?.total).toBe(1);
|
|
55
95
|
});
|
|
56
96
|
|
|
97
|
+
it('updates run status without changing message count or sort order', () => {
|
|
98
|
+
const updated = updateNcpSessionRunStatusList(createSessionsList(), {
|
|
99
|
+
sessionKey: 'session-2',
|
|
100
|
+
status: 'running'
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
expect(updated?.sessions.map((session) => session.sessionId)).toEqual(['session-1', 'session-2']);
|
|
104
|
+
expect(updated?.sessions[1]).toMatchObject({
|
|
105
|
+
sessionId: 'session-2',
|
|
106
|
+
messageCount: 2,
|
|
107
|
+
status: 'running'
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
57
111
|
it('applies realtime upsert/delete events to every ncp-sessions query cache entry', () => {
|
|
58
112
|
const queryClient = new QueryClient();
|
|
59
113
|
queryClient.setQueryData(['ncp-sessions', 200], createSessionsList());
|
|
60
114
|
|
|
115
|
+
applyNcpSessionRealtimeEvent(queryClient, {
|
|
116
|
+
type: 'session.run-status',
|
|
117
|
+
payload: {
|
|
118
|
+
sessionKey: 'session-1',
|
|
119
|
+
status: 'running'
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
expect(queryClient.getQueryData<NcpSessionsListView>(['ncp-sessions', 200])?.sessions[0]?.status).toBe('running');
|
|
124
|
+
|
|
61
125
|
applyNcpSessionRealtimeEvent(queryClient, {
|
|
62
126
|
type: 'session.summary.upsert',
|
|
63
127
|
payload: {
|
|
@@ -5,6 +5,25 @@ function sortSessionSummaries(summaries: readonly NcpSessionSummaryView[]): NcpS
|
|
|
5
5
|
return [...summaries].sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
function shouldReplaceSessionSummary(
|
|
9
|
+
current: NcpSessionSummaryView,
|
|
10
|
+
next: NcpSessionSummaryView
|
|
11
|
+
): boolean {
|
|
12
|
+
const timeOrder = next.updatedAt.localeCompare(current.updatedAt);
|
|
13
|
+
if (timeOrder > 0) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
if (timeOrder < 0) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (current.status === next.status) {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return next.status === 'idle';
|
|
25
|
+
}
|
|
26
|
+
|
|
8
27
|
export function upsertNcpSessionSummaryList(
|
|
9
28
|
current: NcpSessionsListView | undefined,
|
|
10
29
|
summary: NcpSessionSummaryView
|
|
@@ -16,7 +35,9 @@ export function upsertNcpSessionSummaryList(
|
|
|
16
35
|
const existingIndex = current.sessions.findIndex((session) => session.sessionId === summary.sessionId);
|
|
17
36
|
const nextSessions =
|
|
18
37
|
existingIndex >= 0
|
|
19
|
-
? current.sessions.map((session, index) =>
|
|
38
|
+
? current.sessions.map((session, index) =>
|
|
39
|
+
index === existingIndex && shouldReplaceSessionSummary(session, summary) ? summary : session
|
|
40
|
+
)
|
|
20
41
|
: [...current.sessions, summary];
|
|
21
42
|
const sortedSessions = sortSessionSummaries(nextSessions);
|
|
22
43
|
|
|
@@ -62,6 +83,51 @@ export function upsertNcpSessionSummaryInQueryClient(
|
|
|
62
83
|
);
|
|
63
84
|
}
|
|
64
85
|
|
|
86
|
+
export function updateNcpSessionRunStatusList(
|
|
87
|
+
current: NcpSessionsListView | undefined,
|
|
88
|
+
payload: { sessionKey: string; status: 'running' | 'idle' }
|
|
89
|
+
): NcpSessionsListView | undefined {
|
|
90
|
+
if (!current) {
|
|
91
|
+
return current;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const normalizedSessionKey = payload.sessionKey.trim();
|
|
95
|
+
if (!normalizedSessionKey) {
|
|
96
|
+
return current;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
let changed = false;
|
|
100
|
+
const nextSessions = current.sessions.map((session) => {
|
|
101
|
+
if (session.sessionId !== normalizedSessionKey || session.status === payload.status) {
|
|
102
|
+
return session;
|
|
103
|
+
}
|
|
104
|
+
changed = true;
|
|
105
|
+
return {
|
|
106
|
+
...session,
|
|
107
|
+
status: payload.status
|
|
108
|
+
};
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
if (!changed) {
|
|
112
|
+
return current;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
...current,
|
|
117
|
+
sessions: nextSessions
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function updateNcpSessionRunStatusInQueryClient(
|
|
122
|
+
queryClient: QueryClient | undefined,
|
|
123
|
+
payload: { sessionKey: string; status: 'running' | 'idle' }
|
|
124
|
+
): void {
|
|
125
|
+
queryClient?.setQueriesData<NcpSessionsListView>(
|
|
126
|
+
{ queryKey: ['ncp-sessions'] },
|
|
127
|
+
(current) => updateNcpSessionRunStatusList(current, payload)
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
65
131
|
export function deleteNcpSessionSummaryInQueryClient(
|
|
66
132
|
queryClient: QueryClient | undefined,
|
|
67
133
|
sessionKey: string
|
|
@@ -74,8 +140,12 @@ export function deleteNcpSessionSummaryInQueryClient(
|
|
|
74
140
|
|
|
75
141
|
export function applyNcpSessionRealtimeEvent(
|
|
76
142
|
queryClient: QueryClient | undefined,
|
|
77
|
-
event: Extract<WsEvent, { type: 'session.summary.upsert' | 'session.summary.delete' }>
|
|
143
|
+
event: Extract<WsEvent, { type: 'session.run-status' | 'session.summary.upsert' | 'session.summary.delete' }>
|
|
78
144
|
): void {
|
|
145
|
+
if (event.type === 'session.run-status') {
|
|
146
|
+
updateNcpSessionRunStatusInQueryClient(queryClient, event.payload);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
79
149
|
if (event.type === 'session.summary.upsert') {
|
|
80
150
|
upsertNcpSessionSummaryInQueryClient(queryClient, event.payload.summary);
|
|
81
151
|
return;
|
package/src/api/types.ts
CHANGED
|
@@ -569,6 +569,7 @@ export type ConfigActionExecuteResult = {
|
|
|
569
569
|
export type WsEvent =
|
|
570
570
|
| { type: 'config.updated'; payload: { path: string } }
|
|
571
571
|
| { type: 'session.updated'; payload: { sessionKey: string } }
|
|
572
|
+
| { type: 'session.run-status'; payload: { sessionKey: string; status: 'running' | 'idle' } }
|
|
572
573
|
| { type: 'session.summary.upsert'; payload: { summary: NcpSessionSummaryView } }
|
|
573
574
|
| { type: 'session.summary.delete'; payload: { sessionKey: string } }
|
|
574
575
|
| { type: 'config.reload.started'; payload?: Record<string, unknown> }
|
|
@@ -141,6 +141,46 @@ it("maps tool lifecycle statuses into visible card state feedback", () => {
|
|
|
141
141
|
});
|
|
142
142
|
});
|
|
143
143
|
|
|
144
|
+
it("renders spawn tool cards from structured subagent status updates", () => {
|
|
145
|
+
const adapted = adapt([
|
|
146
|
+
{
|
|
147
|
+
id: "assistant-subagent",
|
|
148
|
+
role: "assistant",
|
|
149
|
+
parts: [
|
|
150
|
+
{
|
|
151
|
+
type: "tool-invocation",
|
|
152
|
+
toolInvocation: {
|
|
153
|
+
status: ToolInvocationStatus.RESULT,
|
|
154
|
+
toolCallId: "spawn-call-1",
|
|
155
|
+
toolName: "spawn",
|
|
156
|
+
args: '{"label":"Verifier","task":"Verify 1+1=2"}',
|
|
157
|
+
result: {
|
|
158
|
+
kind: "nextclaw.subagent_run",
|
|
159
|
+
runId: "subagent-1",
|
|
160
|
+
label: "Verifier",
|
|
161
|
+
task: "Verify 1+1=2",
|
|
162
|
+
status: "completed",
|
|
163
|
+
result: "Verified 1+1=2.",
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
},
|
|
169
|
+
] as unknown as ChatMessageSource[]);
|
|
170
|
+
|
|
171
|
+
expect(adapted[0]?.parts[0]).toMatchObject({
|
|
172
|
+
type: "tool-card",
|
|
173
|
+
card: {
|
|
174
|
+
toolName: "spawn",
|
|
175
|
+
summary: "label: Verifier · task: Verify 1+1=2",
|
|
176
|
+
output: "Verified 1+1=2.",
|
|
177
|
+
statusTone: "success",
|
|
178
|
+
statusLabel: "Completed",
|
|
179
|
+
titleLabel: "Tool Result",
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
144
184
|
it("maps non-standard roles back to the generic message role", () => {
|
|
145
185
|
const adapted = adapt([
|
|
146
186
|
{
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
summarizeToolArgs,
|
|
4
4
|
type ToolCard,
|
|
5
5
|
} from "@/lib/chat-message";
|
|
6
|
+
import { buildSubagentToolCard } from "@/components/chat/adapters/chat-message.subagent-tool-card";
|
|
6
7
|
import type {
|
|
7
8
|
ChatMessageRole,
|
|
8
9
|
ChatMessageViewModel,
|
|
@@ -386,6 +387,16 @@ export function adaptChatMessage(
|
|
|
386
387
|
if (assetFileView) {
|
|
387
388
|
return assetFileView;
|
|
388
389
|
}
|
|
390
|
+
const subagentToolCard = buildSubagentToolCard({
|
|
391
|
+
invocation,
|
|
392
|
+
texts: params.texts,
|
|
393
|
+
});
|
|
394
|
+
if (subagentToolCard) {
|
|
395
|
+
return {
|
|
396
|
+
type: "tool-card" as const,
|
|
397
|
+
card: buildToolCard(subagentToolCard, params.texts),
|
|
398
|
+
};
|
|
399
|
+
}
|
|
389
400
|
const statusView = resolveToolCardStatus({
|
|
390
401
|
status: invocation.status,
|
|
391
402
|
error: invocation.error,
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import {
|
|
2
|
+
stringifyUnknown,
|
|
3
|
+
summarizeToolArgs,
|
|
4
|
+
type ToolCard,
|
|
5
|
+
} from "@/lib/chat-message";
|
|
6
|
+
import type { ChatToolPartViewModel } from "@nextclaw/agent-chat-ui";
|
|
7
|
+
|
|
8
|
+
type ToolCardViewSource = ToolCard & {
|
|
9
|
+
statusTone: ChatToolPartViewModel["statusTone"];
|
|
10
|
+
statusLabel: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
type SpawnToolInvocation = {
|
|
14
|
+
toolName: string;
|
|
15
|
+
toolCallId?: string;
|
|
16
|
+
args?: unknown;
|
|
17
|
+
result?: unknown;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type SubagentToolCardTexts = {
|
|
21
|
+
toolStatusRunningLabel: string;
|
|
22
|
+
toolStatusCompletedLabel: string;
|
|
23
|
+
toolStatusFailedLabel: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
type SubagentRunResult = {
|
|
27
|
+
runId?: string;
|
|
28
|
+
label?: string;
|
|
29
|
+
task?: string;
|
|
30
|
+
status?: string;
|
|
31
|
+
result?: unknown;
|
|
32
|
+
message?: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
36
|
+
return typeof value === "object" && value !== null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function readOptionalString(value: unknown): string | null {
|
|
40
|
+
if (typeof value !== "string") {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const trimmed = value.trim();
|
|
44
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function readSubagentRunResult(value: unknown): SubagentRunResult | null {
|
|
48
|
+
if (!isRecord(value)) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
if (value.kind === "nextclaw.subagent_run") {
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
if (typeof value.runId === "string" && typeof value.status === "string") {
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function buildSubagentToolCard(params: {
|
|
61
|
+
invocation: SpawnToolInvocation;
|
|
62
|
+
texts: SubagentToolCardTexts;
|
|
63
|
+
}): ToolCardViewSource | null {
|
|
64
|
+
if (params.invocation.toolName !== "spawn") {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const subagentRun = readSubagentRunResult(params.invocation.result);
|
|
69
|
+
if (!subagentRun) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const detailParts = [
|
|
74
|
+
readOptionalString(subagentRun.label)
|
|
75
|
+
? `label: ${subagentRun.label?.trim()}`
|
|
76
|
+
: null,
|
|
77
|
+
readOptionalString(subagentRun.task)
|
|
78
|
+
? `task: ${subagentRun.task?.trim()}`
|
|
79
|
+
: null,
|
|
80
|
+
].filter((value): value is string => Boolean(value));
|
|
81
|
+
const normalizedStatus = readOptionalString(subagentRun.status)?.toLowerCase();
|
|
82
|
+
const output =
|
|
83
|
+
(typeof subagentRun.result !== "undefined"
|
|
84
|
+
? stringifyUnknown(subagentRun.result).trim()
|
|
85
|
+
: "") ||
|
|
86
|
+
readOptionalString(subagentRun.message) ||
|
|
87
|
+
undefined;
|
|
88
|
+
|
|
89
|
+
if (normalizedStatus === "failed") {
|
|
90
|
+
return {
|
|
91
|
+
kind: "result",
|
|
92
|
+
name: params.invocation.toolName,
|
|
93
|
+
detail: detailParts.join(" · ") || summarizeToolArgs(params.invocation.args),
|
|
94
|
+
text: output,
|
|
95
|
+
callId: params.invocation.toolCallId || undefined,
|
|
96
|
+
hasResult: Boolean(output),
|
|
97
|
+
statusTone: "error",
|
|
98
|
+
statusLabel: params.texts.toolStatusFailedLabel,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (normalizedStatus === "completed") {
|
|
103
|
+
return {
|
|
104
|
+
kind: "result",
|
|
105
|
+
name: params.invocation.toolName,
|
|
106
|
+
detail: detailParts.join(" · ") || summarizeToolArgs(params.invocation.args),
|
|
107
|
+
text: output,
|
|
108
|
+
callId: params.invocation.toolCallId || undefined,
|
|
109
|
+
hasResult: Boolean(output),
|
|
110
|
+
statusTone: "success",
|
|
111
|
+
statusLabel: params.texts.toolStatusCompletedLabel,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
kind: "result",
|
|
117
|
+
name: params.invocation.toolName,
|
|
118
|
+
detail: detailParts.join(" · ") || summarizeToolArgs(params.invocation.args),
|
|
119
|
+
text: output,
|
|
120
|
+
callId: params.invocation.toolCallId || undefined,
|
|
121
|
+
hasResult: Boolean(output),
|
|
122
|
+
statusTone: "running",
|
|
123
|
+
statusLabel: params.texts.toolStatusRunningLabel,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
@@ -23,7 +23,6 @@ import { resolveSessionTypeLabel } from '@/components/chat/useChatSessionTypeSta
|
|
|
23
23
|
import { useConfirmDialog } from '@/hooks/useConfirmDialog';
|
|
24
24
|
import { normalizeRequestedSkills } from '@/lib/chat-runtime-utils';
|
|
25
25
|
import { appClient } from '@/transport';
|
|
26
|
-
import { resolveNcpChatRealtimeReloadAction } from '@/components/chat/ncp/ncp-chat-realtime-reload';
|
|
27
26
|
|
|
28
27
|
function buildNcpSendMetadata(payload: {
|
|
29
28
|
model?: string;
|
|
@@ -71,7 +70,7 @@ export function NcpChatPage({ view }: ChatPageProps) {
|
|
|
71
70
|
const { sessionId: routeSessionIdParam } = useParams<{ sessionId?: string }>();
|
|
72
71
|
const threadRef = useRef<HTMLDivElement | null>(null);
|
|
73
72
|
const selectedSessionKeyRef = useRef<string | null>(selectedSessionKey);
|
|
74
|
-
const
|
|
73
|
+
const sessionStreamAttachInFlightRef = useRef(false);
|
|
75
74
|
const routeSessionKey = useMemo(
|
|
76
75
|
() => parseSessionKeyFromRoute(routeSessionIdParam),
|
|
77
76
|
[routeSessionIdParam]
|
|
@@ -158,48 +157,33 @@ export function NcpChatPage({ view }: ChatPageProps) {
|
|
|
158
157
|
const lastSendError = agent.hydrateError?.message ?? agent.snapshot.error?.message ?? null;
|
|
159
158
|
|
|
160
159
|
useEffect(() => {
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
isHydrating: agent.isHydrating,
|
|
164
|
-
isRunning: agent.isRunning,
|
|
165
|
-
isSending: agent.isSending,
|
|
166
|
-
});
|
|
167
|
-
if (action === 'defer') {
|
|
168
|
-
pendingRealtimeReloadRef.current = true;
|
|
160
|
+
const attachRealtimeSessionStream = () => {
|
|
161
|
+
if (sessionStreamAttachInFlightRef.current) {
|
|
169
162
|
return;
|
|
170
163
|
}
|
|
171
|
-
if (
|
|
172
|
-
pendingRealtimeReloadRef.current = false;
|
|
164
|
+
if (agent.isHydrating || agent.isRunning || agent.isSending) {
|
|
173
165
|
return;
|
|
174
166
|
}
|
|
175
|
-
|
|
176
|
-
|
|
167
|
+
|
|
168
|
+
sessionStreamAttachInFlightRef.current = true;
|
|
169
|
+
void ncpClient
|
|
170
|
+
.stream({ sessionId: activeSessionId })
|
|
171
|
+
.catch(() => undefined)
|
|
172
|
+
.finally(() => {
|
|
173
|
+
sessionStreamAttachInFlightRef.current = false;
|
|
174
|
+
});
|
|
177
175
|
};
|
|
178
176
|
|
|
179
177
|
return appClient.subscribe((event) => {
|
|
180
|
-
if (
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
187
|
-
if (event.type === 'session.updated' && event.payload.sessionKey === activeSessionId) {
|
|
188
|
-
flushRealtimeReload();
|
|
178
|
+
if (
|
|
179
|
+
event.type === 'session.run-status' &&
|
|
180
|
+
event.payload.sessionKey === activeSessionId &&
|
|
181
|
+
event.payload.status === 'running'
|
|
182
|
+
) {
|
|
183
|
+
attachRealtimeSessionStream();
|
|
189
184
|
}
|
|
190
185
|
});
|
|
191
|
-
}, [activeSessionId, agent.isHydrating, agent.isRunning, agent.isSending,
|
|
192
|
-
|
|
193
|
-
useEffect(() => {
|
|
194
|
-
if (!pendingRealtimeReloadRef.current) {
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
if (agent.isHydrating || agent.isRunning || agent.isSending) {
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
pendingRealtimeReloadRef.current = false;
|
|
201
|
-
void agent.reloadSeed();
|
|
202
|
-
}, [agent.isHydrating, agent.isRunning, agent.isSending, agent.reloadSeed]);
|
|
186
|
+
}, [activeSessionId, agent.isHydrating, agent.isRunning, agent.isSending, ncpClient]);
|
|
203
187
|
|
|
204
188
|
useEffect(() => {
|
|
205
189
|
presenter.chatStreamActionsManager.bind({
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
## 目录预算豁免
|
|
2
|
-
- 原因:配置中心目录按配置面板维度组织,每个面板都需要独立入口、局部测试与装配文件;当前结构受 UI
|
|
2
|
+
- 原因:配置中心目录按配置面板维度组织,每个面板都需要独立入口、局部测试与装配文件;当前结构受 UI 信息架构约束,需要保留达到或超过 `12` 个直接代码文件的扁平集合。
|
|
@@ -62,7 +62,11 @@ function handleRealtimeEvent(
|
|
|
62
62
|
handleConfigUpdatedEvent(queryClient, configPath);
|
|
63
63
|
return;
|
|
64
64
|
}
|
|
65
|
-
if (
|
|
65
|
+
if (
|
|
66
|
+
event.type === 'session.run-status' ||
|
|
67
|
+
event.type === 'session.summary.upsert' ||
|
|
68
|
+
event.type === 'session.summary.delete'
|
|
69
|
+
) {
|
|
66
70
|
applyNcpSessionRealtimeEvent(queryClient, event);
|
|
67
71
|
return;
|
|
68
72
|
}
|