@datalayer/agent-runtimes 1.5.42 → 1.5.43
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-embed/datalayer-app-main.js +1 -1
- package/lib/apps/apps/sessionRules.d.ts +14 -1
- package/lib/apps/apps/sessionRules.js +68 -2
- package/lib/apps/core/canvasParts.d.ts +7 -1
- package/lib/apps/plugins/app-rules/AppRulesCard.js +2 -2
- package/lib/apps/plugins/canvas-parts/index.d.ts +9 -1
- package/lib/apps/plugins/canvas-parts/index.js +28 -0
- package/package.json +1 -1
|
@@ -4016,7 +4016,7 @@ ${Mu}`:ju,inputs:Mu,clear:Ou?[]:[DRAFT.path],loop:Lu()}:{refused:"Nothing to sen
|
|
|
4016
4016
|
`);await navigator.clipboard?.writeText(ju),Du(!0)},[$u.user,$u.assistant]);if(Ru==="panel"||$u.status==="idle"||!$u.user||Pu===$u.id)return null;const Bu=$u.status==="thinking"||$u.status==="streaming";return jsxRuntimeExports.jsxs(Box,{"data-turn-panel":"",borderTop:"1px solid",borderColor:"border.muted",px:3,pt:2,pb:Au==="none"?2:1,display:"flex",flexDirection:"column",gap:2,bg:"canvas.default",children:[jsxRuntimeExports.jsx(Box,{display:"flex",justifyContent:"flex-end",children:jsxRuntimeExports.jsx(Text$3,{sx:{fontSize:0,lineHeight:1.45,px:2,py:1,borderRadius:"10px",bg:"accent.subtle",color:"fg.default",maxWidth:"85%",overflow:"hidden",display:"-webkit-box",WebkitLineClamp:2,WebkitBoxOrient:"vertical"},children:$u.user})}),jsxRuntimeExports.jsxs(Box,{display:"flex",gap:2,alignItems:"flex-start",children:[jsxRuntimeExports.jsx(Box,{ref:Iu,flex:"1 1 auto",minWidth:0,maxHeight:MAX_HEIGHT,overflowY:"auto",fontSize:0,lineHeight:1.5,color:$u.assistant?"fg.default":"fg.muted",whiteSpace:$u.assistant?"normal":"pre-wrap",overflowWrap:"anywhere",children:$u.assistant?jsxRuntimeExports.jsx(Box,{sx:{...streamdownMarkdownStyles,fontSize:0},children:jsxRuntimeExports.jsx(_o$1,{rehypePlugins:REHYPE_PLUGINS_WITHOUT_HTML,children:normalizeAssistantMarkdown($u.assistant)})}):$u.activity?jsxRuntimeExports.jsx(Text$3,{sx:{color:"fg.muted",fontStyle:"italic"},children:$u.activity}):Bu?jsxRuntimeExports.jsx(Thinking,{}):$u.status==="error"?"The agent could not answer.":"Done — the result is in the page."}),jsxRuntimeExports.jsx(IconButton,{icon:CommentDiscussionIcon,size:"small",variant:"invisible","aria-label":"Open the conversation",onClick:openPagePanel,sx:{color:"fg.muted",flexShrink:0}})]}),$u.assistant&&$u.activity?jsxRuntimeExports.jsx(Text$3,{sx:{fontSize:0,color:"fg.muted",fontStyle:"italic"},children:$u.activity}):null,Au==="full"?jsxRuntimeExports.jsx(TurnFooter,{usage:$u.usage,live:Bu,latest:!0,padding:0,onCopy:Lu,onRemove:()=>Ou($u.id)}):Au==="actions"?jsxRuntimeExports.jsxs(Box,{display:"flex",justifyContent:"flex-end",gap:1,mt:-1,children:[jsxRuntimeExports.jsx(IconButton,{icon:Mu?CheckIcon:CopyIcon,size:"small",variant:"invisible","aria-label":Mu?"Copied":"Copy this turn",onClick:()=>{Lu()},sx:{color:"fg.muted"}}),jsxRuntimeExports.jsx(IconButton,{icon:XIcon,size:"small",variant:"invisible","aria-label":"Dismiss until the next turn",onClick:()=>Ou($u.id),sx:{color:"fg.muted"}})]}):null]})}const SPLIT_DEFAULT_SHARE=.4,SPLIT_MIN_SHARE=.2,SPLIT_MAX_SHARE=.8,SPLIT_KEY_STEP=.05;function clampSplitShare(Au){return Number.isFinite(Au)?Math.min(SPLIT_MAX_SHARE,Math.max(SPLIT_MIN_SHARE,Au)):SPLIT_DEFAULT_SHARE}function splitShareForKey(Au,lo){switch(lo){case"ArrowLeft":return clampSplitShare(Au-SPLIT_KEY_STEP);case"ArrowRight":return clampSplitShare(Au+SPLIT_KEY_STEP);case"Home":return SPLIT_MIN_SHARE;case"End":return SPLIT_MAX_SHARE;default:return null}}function SplitLayout({editors:Au,hasEditor:lo,transcript:$u,prompt:Ru,chips:Iu,picker:Pu,transient:Ou}){const[Mu,Du]=reactExports.useState(SPLIT_DEFAULT_SHARE),Lu=reactExports.useRef(null),Bu=reactExports.useRef(!1),ju=reactExports.useCallback(Gu=>{const Wu=Lu.current?.getBoundingClientRect();return!Wu||Wu.width<=0?null:clampSplitShare((Gu-Wu.left)/Wu.width)},[]),Fu=Gu=>{Bu.current=!0,Gu.currentTarget.setPointerCapture?.(Gu.pointerId),Gu.preventDefault()},zu=Gu=>{if(!Bu.current)return;const Wu=ju(Gu.clientX);Wu!==null&&Du(Wu)},Uu=Gu=>{Bu.current=!1,Gu.currentTarget.releasePointerCapture?.(Gu.pointerId)},Hu=Gu=>{const Wu=splitShareForKey(Mu,Gu.key);Wu!==null&&(Gu.preventDefault(),Du(Wu))},Vu=Math.round(Mu*100);return jsxRuntimeExports.jsxs(Box,{"data-testid":"loop-split-layout",flex:"1 1 auto",minHeight:0,display:"flex",flexDirection:"column",children:[Pu,jsxRuntimeExports.jsxs(Box,{ref:Lu,flex:"1 1 auto",minHeight:0,display:"flex",position:"relative",overflow:"hidden",children:[jsxRuntimeExports.jsxs(Box,{"data-testid":"loop-split-conversation",flex:lo?`0 0 ${Vu}%`:"1 1 auto",minWidth:0,minHeight:0,display:"flex",flexDirection:"column",ml:lo?"-1px":0,children:[$u,Iu,Ru]}),lo?jsxRuntimeExports.jsx(Box,{role:"separator","aria-orientation":"vertical","aria-label":"Resize the conversation and the page","aria-valuemin":Math.round(SPLIT_MIN_SHARE*100),"aria-valuemax":Math.round(SPLIT_MAX_SHARE*100),"aria-valuenow":Vu,tabIndex:0,onPointerDown:Fu,onPointerMove:zu,onPointerUp:Uu,onPointerCancel:Uu,onKeyDown:Hu,flex:"0 0 7px",mx:"-3px",zIndex:1,cursor:"col-resize",display:"flex",justifyContent:"center",outline:"none",touchAction:"none",sx:{"&::before":{content:'""',width:"1px",height:"100%",bg:"border.default"},"&:hover::before, &:focus-visible::before":{width:"3px",bg:"accent.emphasis"}}}):null,jsxRuntimeExports.jsx(Box,{"data-testid":"loop-split-page",sx:lo?{flex:"1 1 0",minWidth:0,minHeight:0,display:"flex",...paneOpening("right")}:{position:"absolute",inset:0,visibility:"hidden",pointerEvents:"none",zIndex:-1},children:Au})]}),Ou]})}const LOOP_PAGE_LAYOUT_PLUGIN_NAME="@datalayer/loop-plugin-page-layout",NO_TURN$4=signal$2({id:0,status:"idle"}),pageLayoutChatMode=signal$2("floating-draggable"),promptStance=signal$2("floating-top");let floatingStance="floating-top";const PAGE_LAYOUT_DISABLED_MODES=["assistant"];function pageLayoutForChatMode(Au){switch(Au){case"sidebar":return{bandMode:"panel",panelMode:"docked"};case"floating":return{bandMode:"panel",panelMode:"overlay"};case"floating-small":return{bandMode:"panel",panelMode:"popup"};default:return{bandMode:"floating",panelMode:"docked"}}}function setPageLayoutChatMode(Au){pageLayoutChatMode.value=Au;const{bandMode:lo}=pageLayoutForChatMode(Au);promptStance.value=lo==="floating"?floatingStance:"docked-top",lo==="panel"&&openPagePanel()}function ChatModeAction(){const Au=useSignalValue$1(pageLayoutChatMode);return jsxRuntimeExports.jsx(ChatViewModeToggle,{value:Au,onChange:setPageLayoutChatMode,disabledModes:PAGE_LAYOUT_DISABLED_MODES})}function LoopPageLayout({parts:Au,promptMode:lo,pageSize:$u}){const Ru=useContributions(LoopChatTurn),Iu=useSignalValue$1(Ru[0]?.value.turn??NO_TURN$4),Pu=useSignalValue$1(pageLayoutChatMode),Ou=lo==="floating"?pageLayoutForChatMode(Pu):{bandMode:"docked",panelMode:"docked"};return jsxRuntimeExports.jsx(PageLayout,{page:Au.editors,hasPage:Au.hasEditor,panel:Au.transcript,band:Au.prompt,chips:Au.chips,picker:Au.picker,transient:Au.transient,activity:Iu.activity,bandMode:Ou.bandMode,panelMode:Ou.panelMode,pageSize:$u})}function ConversationToggle(){return jsxRuntimeExports.jsx(PagePanelToggle,{panelName:"conversation",icon:CommentDiscussionIcon,sx:{order:2}})}const LoopPageLayoutPlugin={name:LOOP_PAGE_LAYOUT_PLUGIN_NAME,config:{arrangement:"page",turnPanel:"below",turnPanelFooter:"full",prompt:"docked",promptAnchor:"top",pageSize:void 0},displayName:"Page layout",description:"The editor — or, in the chat view, the conversation — on a centred sheet, the prompt docked above it at the same width or floating over it as a draggable card, the current turn under the prompt, the conversation in a side panel; or, split, the conversation and the work side by side with a hairline to drag.",octicon:"file",emoji:"📄",build:({config:Au,...lo})=>{if(Au.arrangement==="split")return lo.contribute(LoopChatLayout,{id:"split-layout",prompt:"docked",Component:SplitLayout},{id:"split-layout"}),{components:[]};const $u=Au.prompt,Ru=Au.pageSize;floatingStance=Au.promptAnchor==="bottom"?"floating-bottom":"floating-top",$u==="floating"&&(promptStance.value=floatingStance);const Iu=Pu=>jsxRuntimeExports.jsx(LoopPageLayout,{parts:Pu,promptMode:$u,pageSize:Ru});if(lo.contribute(LoopChatLayout,{id:"page-layout",prompt:$u==="floating"?floatingStance:"docked-top",promptStance:$u==="floating"?promptStance:void 0,Component:Iu},{id:"page-layout"}),Au.turnPanel!=="none"){const Pu=Au.turnPanelFooter,Ou=()=>jsxRuntimeExports.jsx(TurnPanel,{footer:Pu});lo.contribute(LoopPromptPanel,{id:"page-layout-turn",placement:Au.turnPanel,order:0,Component:Ou},{id:"page-layout-turn"})}return{components:[{id:"page-layout-conversation-toggle",slot:LoopSlots.header,Component:ConversationToggle},...$u==="floating"?[{id:"page-layout-chat-mode",slot:LoopSlots.promptAction,Component:ChatModeAction}]:[]]}}};function loopPlugins(Au={}){const{serverUrl:lo,target:$u="browser",defaultEditor:Ru,showViewSelector:Iu=!0,hideChatHeader:Pu=!1,chatHeaderButtons:Ou=!1,promptPlacement:Mu,autoFocusPrompt:Du=!0,suggestionLabels:Lu=!0,fullScreenTopOffset:Bu=0,showAgentVariants:ju=!1,agentSummary:Fu=!0,teamId:zu,teamPicker:Uu=!0,pageLayout:Hu=!1,pageLayoutArrangement:Vu="page",pageLayoutTurnPanel:Gu="below",pageLayoutPrompt:Wu="docked",pageLayoutPromptAnchor:Ku="top",pageLayoutSize:Ju,pageLayoutTurnPanelFooter:Yu="full",localAgent:Xu,localAgentSpec:ed,datalayerAgentSpecId:td,datalayerCreatePayload:nd,datalayerVisitors:od,datalayerKept:sd,embedToken:ad,targetFixed:pd,themeVariant:gd,themeOverrides:_d,colorMode:yd,presence:bd,showTokenUsage:wd,voice:Ed,thread:cd,floatingPrompt:dd=!1,editorSelector:md=!1,editors:ld=!0,conversation:hd=!0,commandPalette:vd=!1,pluginsPanel:Sd=!1,windowFrame:kd=!1,firstPromptHook:Rd}=Au,Od=Ru??(ld?"notebook":"none");return hd?[configurePlugin(ChatPlugin,{defaultSurface:Od,showSurfaceSelector:ld&&Iu&&!md,hideHeader:Pu,headerButtons:{newChat:Ou,clear:Ou},promptPlacement:dd?"floating":Mu,autoFocusPrompt:Du,suggestionLabels:Lu,fullScreenTopOffset:Bu,themeVariant:gd,themeOverrides:_d,colorMode:yd,presence:bd,showTokenUsage:wd,...Ed?{voice:Ed}:{},...cd?{thread:cd}:{}}),configurePlugin(InputPromptPlugin,{firstPromptHook:Rd}),ChatHeaderPlugin,A2uiSurfacePlugin,PapSignInPlugin,ShieldDisplaysPlugin,ChatViewPlugin,...ld?[NotebookViewPlugin,DocumentViewPlugin]:[],configurePlugin(AgentsPlugin,{serverUrl:lo,target:$u,datalayerAgentSpecId:td,datalayerCreatePayload:nd,datalayerVisitors:od,datalayerKept:sd,embedToken:ad,targetFixed:pd,showAgentVariants:ju,showAgentSummary:Fu,teamId:zu,showTeamPicker:Uu,localAgent:Xu??(ed?{createPayload:{description:`Local agent from the ${ed} spec`,agent_library:"pydantic-ai",agent_spec_id:ed,enable_codemode:!1}}:void 0)}),...ld?[NotebookExtension,DocumentExtension]:[],A2uiPlugin,AgentspecsPlugin,ModelsPlugin,ThemePlugin,SubagentActivityPlugin,configurePlugin(ShellPlugin,{defaultEditor:Od,showSelector:ld&&md}),...dd?[PromptPlugin]:[],...Hu?[configurePlugin(LoopPageLayoutPlugin,{arrangement:Vu,turnPanel:Gu,turnPanelFooter:Yu,prompt:Wu,promptAnchor:Ku,pageSize:Ju})]:[],...vd?[LoopCommandsPlugin]:[],...Sd?[PluginsPanelPlugin]:[],...kd?[WindowFramePlugin]:[]]:[ThemePlugin,configurePlugin(ShellPlugin,{defaultEditor:"none",showSelector:!1}),...vd?[LoopCommandsPlugin]:[],...Sd?[PluginsPanelPlugin]:[],...kd?[WindowFramePlugin]:[]]}function LoopEmbed({agentId:Au="",showHeader:lo=!1,chatHeaderActions:$u,headerActions:Ru,headerContainer:Iu,frameTitle:Pu,frameHeight:Ou,sidebarRail:Mu=!1,plugins:Du=[],...Lu}){const Bu=reactExports.useRef(Lu.firstPromptHook);Bu.current=Lu.firstPromptHook;const ju=reactExports.useMemo(()=>buildLoopReactor([...loopPlugins({...Lu,windowFrame:Lu.windowFrame||Pu!==void 0,firstPromptHook:()=>Bu.current?.()}),...Du]),[Lu.serverUrl,Lu.target,Lu.datalayerAgentSpecId,Lu.targetFixed,Lu.themeVariant,JSON.stringify(Lu.themeOverrides??null),Lu.colorMode,Lu.presence?.name,Lu.presence?.face,Lu.presence?.welcome,Lu.presence?.paused,Lu.showTokenUsage,JSON.stringify({...Lu.voice??{},engines:void 0,token:void 0}),Lu.thread?.id,JSON.stringify(Lu.datalayerCreatePayload??null),JSON.stringify(Lu.datalayerVisitors??null),Lu.datalayerKept?.url,Lu.embedToken,Lu.defaultEditor,Lu.showViewSelector,Lu.hideChatHeader,Lu.chatHeaderButtons,Lu.promptPlacement,Lu.autoFocusPrompt,Lu.suggestionLabels,Lu.fullScreenTopOffset,Lu.showAgentVariants,Lu.agentSummary,Lu.teamId,Lu.teamPicker,Lu.pageLayout,Lu.pageLayoutTurnPanel,Lu.pageLayoutTurnPanelFooter,Lu.pageLayoutPrompt,Lu.pageLayoutPromptAnchor,Lu.pageLayoutSize?.format,Lu.pageLayoutSize?.width,Lu.pageLayoutSize?.height,Lu.localAgentSpec,Lu.floatingPrompt,Lu.editorSelector,Lu.editors,Lu.conversation,Lu.commandPalette,Lu.pluginsPanel,Lu.windowFrame,Pu!==void 0,Du]);useReactor(ju);const Fu=jsxRuntimeExports.jsx(LoopWorkspace,{serverUrl:Lu.serverUrl??"",agentId:Au,reactor:ju,manageReactor:!1,showViewSelector:Lu.showViewSelector,showHeader:lo,chatHeaderActions:$u,headerActions:Ru,headerContainer:Iu,sidebarRail:Mu});return jsxRuntimeExports.jsx(QueryClientProvider,{client:internalQueryClient,children:Pu!==void 0?jsxRuntimeExports.jsx(WindowFrame,{title:Pu,height:Ou,children:Fu}):jsxRuntimeExports.jsx(Box,{height:"100%",minHeight:0,width:"100%",flex:"1 1 auto",minWidth:0,children:Fu})})}const RECORD_KIND_WORDS={session:"Session",tool_call:"Tool call",decision:"Rule",check:"Check",approval:"Approval",output:"Answer",feedback:"Feedback",turn:"Turn",run:"Working",notification:"Notification"};function entryOf(Au){return{uid:String(Au.uid??""),sessionUid:String(Au.session_uid??""),deploymentUid:String(Au.deployment_uid??""),version:Number(Au.version)||0,kind:String(Au.kind??"output"),summary:String(Au.summary??""),payload:Au.payload&&typeof Au.payload=="object"?Au.payload:{},createdAt:String(Au.created_at??"")}}function runEndOf(Au){const lo=Au.kind==="output"?Au.payload.status:void 0;return typeof lo=="string"&&lo in RUN_END_WORDS?lo:""}function entrySaid(Au){return Au.summary||RUN_END_WORDS[runEndOf(Au)]||""}function sessionSentence(Au){const lo=Au.filter(Ou=>Ou.kind==="tool_call").length,$u=Au.filter(Ou=>Ou.kind==="check"),Ru=Au.map(runEndOf).filter(Boolean),Iu=Ru.filter(Ou=>Ou==="answered").length,Pu=[lo===0?"No tool called":`${lo} tool call${lo===1?"":"s"}`];return $u.length&&Pu.push(`${$u.length} step${$u.length===1?"":"s"} a check did not let pass`),Ru[Ru.length-1]==="stopped"?Pu.push("stopped"):Iu&&Pu.push(`${Iu} answer${Iu===1?"":"s"}`),`${Pu.join(", ")}.`}async function read(Au,lo,$u){if(!Au.aiAgentsUrl)throw new Error("The record service is not configured.");const Ru=await fetch(`${Au.aiAgentsUrl.replace(/\/+$/,"")}/api/ai-agents/v1/apps${lo}`,{headers:Au.token?{Authorization:`Bearer ${Au.token}`}:{}}),Iu=await Ru.json().catch(()=>({}));if(!Ru.ok||Iu.success===!1)throw new Error(`${$u}: ${String(Iu.detail??Ru.status)}`);return Iu}async function listSessions(Au,lo,$u=20,Ru=!1){const Iu=await read(Au,`/sessions?app_uid=${encodeURIComponent(lo)}&limit=${$u}${Ru===null?"":`&tests=${Ru}`}`,"The sessions could not be read");return Array.isArray(Iu.sessions)?Iu.sessions.map(entryOf):[]}async function readSession(Au,lo){const $u=await read(Au,`/records?session_uid=${encodeURIComponent(lo)}&limit=500`,"The session could not be read");return Array.isArray($u.entries)?$u.entries.map(entryOf).filter(Ru=>Ru.kind!=="run"):[]}const APP_ACTIVITY_WORDS={title:"Activity",unsaved:"Recorded once it is saved on Datalayer: each session there — its address, or its Preview on Datalayer — is listed here.",signedOut:"Its activity is its owner’s: sign in to Datalayer to read it.",none:"Nothing yet: a session on Datalayer — its address, or the Preview on Datalayer — is recorded here."},NO_TURN$3=signal$2({id:0,status:"idle"});function useRead(Au,lo,$u=!0){const[Ru,Iu]=reactExports.useState({loading:$u});return reactExports.useEffect(()=>{if(!$u)return;let Pu=!1;return Iu(Ou=>({...Ou,loading:!0})),Au().then(Ou=>!Pu&&Iu({value:Ou,loading:!1}),Ou=>!Pu&&Iu({error:Ou instanceof Error?Ou.message:String(Ou),loading:!1})),()=>{Pu=!0}},[lo,$u]),Ru}function Session({context:Au,session:lo}){const[$u,Ru]=reactExports.useState(!1),Iu=useRead(()=>readSession(Au,lo.sessionUid),`${lo.sessionUid}:${Au.token}`,$u);return jsxRuntimeExports.jsxs(Box,{as:"details",onToggle:Pu=>Ru(Pu.currentTarget.open),borderTop:"1px solid",borderColor:"border.muted",py:2,children:[jsxRuntimeExports.jsxs(Box,{as:"summary",cursor:"pointer",fontSize:1,children:[lo.createdAt?jsxRuntimeExports.jsx(RelativeTime,{datetime:lo.createdAt}):"A session",lo.version?` · version ${lo.version}`:"",Iu.value?` · ${sessionSentence(Iu.value)}`:""]}),$u?Iu.loading?jsxRuntimeExports.jsx(StyledSpinner,{size:"small"}):Iu.error?jsxRuntimeExports.jsx(Text$3,{role:"alert",sx:{fontSize:1},children:Iu.error}):jsxRuntimeExports.jsx(Box,{as:"ol",pl:3,mt:2,mb:0,fontSize:1,children:(Iu.value??[]).map(Pu=>jsxRuntimeExports.jsxs(Box,{as:"li",mb:1,children:[jsxRuntimeExports.jsx(Label,{sx:{mr:2},children:RECORD_KIND_WORDS[Pu.kind]??Pu.kind}),entrySaid(Pu)]},Pu.uid))}):null]})}function AppActivity({appUid:Au}){const lo=useIAMStore(ju=>ju.token)??"",$u=useCoreStore(ju=>ju.configuration)?.aiAgentsUrl??"",Ru=useContributions(LoopChatTurn),Iu=useSignalValue$1(Ru[0]?.value.turn??NO_TURN$3),Pu=Iu.status!=="thinking"&&Iu.status!=="streaming",[Ou,Mu]=reactExports.useState(0);reactExports.useEffect(()=>{Pu&&Mu(Iu.id)},[Pu,Iu.id]);const Du={aiAgentsUrl:$u,token:lo},Lu=!!(Au&&lo),Bu=useRead(()=>listSessions(Du,Au??""),`${Au}:${lo}:${$u}:${Ou}`,Lu);return jsxRuntimeExports.jsxs(Box,{"data-testid":"app-activity",children:[jsxRuntimeExports.jsx(Heading$2,{as:"h3",sx:{fontSize:2,mb:2},children:APP_ACTIVITY_WORDS.title}),Au?lo?Bu.loading&&!Bu.value?jsxRuntimeExports.jsx(StyledSpinner,{size:"small"}):Bu.error?jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:1,color:"fg.muted",m:0},children:Bu.error}):(Bu.value??[]).length===0?jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:1,color:"fg.muted",m:0},children:APP_ACTIVITY_WORDS.none}):(Bu.value??[]).map(ju=>jsxRuntimeExports.jsx(Session,{context:Du,session:ju},ju.uid)):jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:1,color:"fg.muted",m:0},children:APP_ACTIVITY_WORDS.signedOut}):jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:1,color:"fg.muted",m:0},children:APP_ACTIVITY_WORDS.unsaved})]})}const APP_ACTIVITY_PLUGIN_NAME="@datalayer/loop-plugin-app-activity";function defineAppActivityPlugin(Au,lo){function $u(){return jsxRuntimeExports.jsx(AppActivity,{appUid:lo})}return{name:`${APP_ACTIVITY_PLUGIN_NAME}-${Au.id}`,displayName:`${Au.name}: activity`,description:`What ${Au.name} did, session by session, from its record.`,octicon:"pulse",build:()=>({components:[{id:"app-activity",slot:LoopSlots.sidebar,order:20,Component:$u,rail:{label:"Activity",icon:PulseIcon}}]})}}const APP_PANEL_WIDTH=380;function elementsByPlace(Au){const lo=Au.filter($u=>$u.where==="page");return{panels:Au.filter($u=>$u.where==="panel"),page:lo.length>0?lo[lo.length-1]:null}}function ElementBody({element:Au,send:lo}){return jsxRuntimeExports.jsx(AnswerShowsView,{display:Au.shows,send:($u,Ru)=>lo!==void 0&&!lo($u,Ru)})}function ElementHeader({element:Au}){return jsxRuntimeExports.jsxs(Box,{display:"flex",alignItems:"center",justifyContent:"space-between",gap:2,mb:2,children:[jsxRuntimeExports.jsx(Text$3,{as:"h2",sx:{fontSize:2,fontWeight:"bold",m:0},children:Au.title}),jsxRuntimeExports.jsx(IconButton,{icon:XIcon,variant:"invisible",size:"small","aria-label":`Close ${Au.title}`,onClick:()=>closeLoopElement(Au.id)})]})}function AppElements({workspace:Au}){const lo=reactExports.useSyncExternalStore(onLoopElements,openLoopElements,openLoopElements),{panels:$u,page:Ru}=elementsByPlace(lo),Iu=Au?.viewControls.send;return $u.length===0&&Ru===null?null:jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[$u.length>0?jsxRuntimeExports.jsx(Box,{as:"aside","aria-label":"Side panel",position:"absolute",top:0,right:0,bottom:0,width:`min(${APP_PANEL_WIDTH}px, 100%)`,zIndex:1,overflowY:"auto",borderLeft:"1px solid",borderColor:"border.default",bg:"canvas.subtle",boxShadow:"shadow.medium",p:3,display:"flex",flexDirection:"column",gap:3,children:$u.map(Pu=>jsxRuntimeExports.jsxs(Box,{as:"section","aria-label":Pu.title,children:[jsxRuntimeExports.jsx(ElementHeader,{element:Pu}),jsxRuntimeExports.jsx(ElementBody,{element:Pu,send:Iu})]},Pu.id))}):null,Ru!==null?jsxRuntimeExports.jsxs(Box,{role:"dialog","aria-label":Ru.title,position:"absolute",inset:0,zIndex:2,overflowY:"auto",bg:"canvas.default",p:[3,4],children:[jsxRuntimeExports.jsx(ElementHeader,{element:Ru}),jsxRuntimeExports.jsx(ElementBody,{element:Ru,send:Iu})]}):null]})}const QUESTION_DISPLAY_TYPE="application/vnd.datalayer.loop.question+json",SchemaForm=reactExports.lazy(()=>__vitePreload(()=>import("./chunks/SchemaForm-CE5NfpBU.js"),[],import.meta.url));function questionOf(Au){const lo=Au.data??{},$u=lo.kind;if(Au.type!==QUESTION_DISPLAY_TYPE||$u!=="text"&&$u!=="choice"&&$u!=="file"&&$u!=="form"||typeof lo.prompt!="string")throw new Error(`Display ${Au.id} is not a question`);const Ru=Iu=>Array.isArray(Iu)?Iu.filter(Pu=>typeof Pu=="string"):[];return{kind:$u,prompt:lo.prompt,options:Ru(lo.options),accept:Ru(lo.accept),form:lo.form&&typeof lo.form=="object"?lo.form:{},answered:typeof lo.answered=="string"?lo.answered:null,answerable:(Au.actions??[]).some(Iu=>Iu.name===ANSWER_ACTION)}}function QuestionView({display:Au,act:lo,upload:$u}){const Ru=questionOf(Au),[Iu,Pu]=reactExports.useState(""),[Ou,Mu]=reactExports.useState(null),Du=Bu=>{if(!lo){Mu("The chat cannot take an answer here.");return}Mu(null),Promise.resolve(lo({action:ANSWER_ACTION,payload:{value:Bu}})).catch(ju=>Mu(ju instanceof Error?ju.message:String(ju)))};if(Ru.answered!==null)return jsxRuntimeExports.jsxs(Text$3,{as:"p",sx:{color:"fg.muted",fontSize:1,m:0},children:["Answered: ",Ru.answered]});const Lu=Ru.answerable&&!!lo;return jsxRuntimeExports.jsxs(Box$1,{display:"flex",flexDirection:"column",gap:2,"data-question-kind":Ru.kind,children:[Ru.kind==="choice"?jsxRuntimeExports.jsx(Box$1,{display:"flex",gap:2,flexWrap:"wrap",children:Ru.options.map(Bu=>jsxRuntimeExports.jsx(ButtonComponent,{size:"small",disabled:!Lu,onClick:()=>Du(Bu),children:Bu},Bu))}):null,Ru.kind==="text"?jsxRuntimeExports.jsxs(Box$1,{as:"form",display:"flex",gap:2,onSubmit:Bu=>{Bu.preventDefault(),Iu.trim()&&Du(Iu.trim())},children:[jsxRuntimeExports.jsx(TextInput$1,{"aria-label":Ru.prompt,value:Iu,disabled:!Lu,onChange:Bu=>Pu(Bu.target.value)}),jsxRuntimeExports.jsx(ButtonComponent,{type:"submit",size:"small",disabled:!Lu||!Iu.trim(),children:"Answer"})]}):null,Ru.kind==="form"?jsxRuntimeExports.jsx(reactExports.Suspense,{fallback:null,children:jsxRuntimeExports.jsx(SchemaForm,{schema:Ru.form,disabled:!Lu,onSubmit:({formData:Bu})=>Du(Bu??{})})}):null,Ru.kind==="file"?jsxRuntimeExports.jsx("input",{type:"file","aria-label":Ru.prompt,accept:Ru.accept.join(",")||void 0,disabled:!Lu||!$u,onChange:Bu=>{const ju=Bu.target.files?.[0];if(ju){if(!$u){Mu("The chat cannot take a file here.");return}Mu(null),$u(ju).then(Fu=>Du(Fu)).catch(Fu=>Mu(Fu instanceof Error?Fu.message:String(Fu)))}}}):null,Ou?jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{color:"danger.fg",fontSize:1,m:0},children:Ou}):null]})}const APP_ELEMENTS_PLUGIN_NAME="@datalayer/loop-plugin-app-elements",AppElementsPlugin={name:APP_ELEMENTS_PLUGIN_NAME,displayName:"Side panel and pages",description:"What an application opens beside the conversation: a side panel, or a page of its own.",octicon:"sidebar-expand",contributes:[contribution(Renderers,defineRenderer({type:QUESTION_DISPLAY_TYPE,component:QuestionView}),{id:"app-elements:question"})],build:()=>({components:[{id:"app-elements",slot:LoopSlots.root,Component:AppElements}]}),register:()=>clearLoopElements},appComponentsPluginName=Au=>`@datalayer/loop-plugin-app-components-${Au}`,hasCustomComponents=Au=>(Au.interface.customComponents??[]).length>0;function defineAppComponentsPlugin(Au,lo){const $u=Au.interface.customComponents??[];if($u.length===0)throw new Error(`“${Au.name}” has no component of its own to contribute.`);return{name:appComponentsPluginName(Au.id),displayName:`${Au.name}'s components`,description:`The components ${Au.name}'s developer wrote: ${$u.map(Ru=>Ru.name).join(", ")}.`,octicon:"apps",contributes:$u.flatMap(Ru=>[contribution(LoopCanvasBlock,{id:Ru.name,ui:`app:${Au.id}`,component:customComponentEntry(Ru,Au.version)},{id:Ru.name}),contribution(LoopA2uiComponent,{id:Ru.name,app:Au.id,implementation:customImplementation(Ru,Au.version,lo,Au.id)},{id:Ru.name})])}}const COMPUTER_PARTS=["browse","files","shell"],COMPUTER_PART_TOOLS={browse:["open_page","read_page","page_screenshot","click_on_page","type_on_page"],files:["list_computer_files","read_computer_file","write_computer_file"],shell:["execute_code","run_skill_script"]},COMPUTER_WORDS={title:"Computer",part:{browse:"Browse",files:"Files",shell:"Shell"},on:"on",off:"off",none:"It has no computer: browse, files and shell are all off. Turn one on in its permissions to give it one.",offSaid:Au=>`Off, so its agent is given no tool for it: ${Au.join(", ")}.`,browser:"Browser",noBrowser:"No browser is installed on its computer.",noPage:"Its browser has opened no page yet.",pageAlt:Au=>`The page its browser shows: ${Au}`,clickToAct:"Click on the page to click there; with the page selected, what you type goes to it.",typeOnPage:"Text to type on the page",type:"Type",address:"An address to open",open:"Open",scrollUp:"Scroll up",scrollDown:"Scroll down",agentHas:"Its agent has the computer.",youHave:"You have the computer: its agent waits for it until you hand it back.",someoneHas:"Somebody else has taken this computer over.",takeOver:"Take over",handBack:"Hand back",run:"Run",yourCode:"Python to run on its computer",terminal:"What ran on it",nothingRan:"Nothing has run on it yet in this conversation.",files:"Files",notStarted:"Its computer starts when its agent first uses it: no file yet.",emptyDirectory:"Empty.",up:"Up",download:"Download",signedOut:"Its computer is shown once you are signed in to Datalayer."};function computerParts(Au){const lo=Au.permissions?.computer;return{browse:!!lo?.browse,files:!!lo?.files,shell:!!lo?.shell}}const hasComputer=Au=>COMPUTER_PARTS.some(lo=>Au[lo]),partsOff=Au=>COMPUTER_PARTS.filter(lo=>!Au[lo]).map(lo=>COMPUTER_WORDS.part[lo].toLowerCase());function partOf(Au){return COMPUTER_PARTS.find(lo=>COMPUTER_PART_TOOLS[lo].includes(Au))}const OUTPUT_LIMIT=4e3;function outputOf(Au){let lo=Au;if(typeof lo=="string")try{const Ru=JSON.parse(lo);Ru&&typeof Ru=="object"&&!Array.isArray(Ru)&&(lo=Ru)}catch{}let $u;if(lo&&typeof lo=="object"&&!Array.isArray(lo)){const Ru=lo,Iu=[typeof Ru.stdout=="string"?Ru.stdout:Ru.output,Ru.stderr,Ru.error??Ru.execution_error??Ru.code_error].filter(Pu=>typeof Pu=="string"&&Pu.trim()!=="");$u=Iu.length>0?Iu.join(`
|
|
4017
4017
|
`):"stdout"in Ru||"output"in Ru?"":JSON.stringify(lo,null,2)}else $u=typeof lo=="string"?lo:lo==null?"":JSON.stringify(lo,null,2);return $u.length>OUTPUT_LIMIT?`${$u.slice(0,OUTPUT_LIMIT)}
|
|
4018
4018
|
… (${($u.length-OUTPUT_LIMIT).toLocaleString("en")} characters more)`:$u}function commandOf(Au){for(const lo of["code","path","script_name","url","selector"]){const $u=Au[lo];if(typeof $u=="string"&&$u.trim())return $u}return Object.keys(Au).length?JSON.stringify(Au):""}function terminalEntries(Au){const lo=[];for(const $u of Au){if($u.role!=="tool")continue;const Ru=partOf($u.name);Ru&&lo.push({tool:$u.name,part:Ru,command:commandOf($u.args??{}),output:$u.result===void 0?void 0:outputOf($u.result)})}return lo}const computerUrl=(Au,lo="")=>`${Au.serverUrl.replace(/\/+$/,"")}/api/v1/apps/agents/${encodeURIComponent(Au.agentId)}/computer${lo}`;function computerFileUrl(Au,lo){const $u=computerUrl(Au,`/file?path=${encodeURIComponent(lo)}`),Ru=(Au.userAppsUrl||"").trim();if(!Ru)return $u;try{const Iu=new URL($u),Pu=new URL(Ru);return Iu.protocol=Pu.protocol,Iu.host=Pu.host,Iu.toString()}catch{return $u}}async function asked(Au,lo,$u={}){const Ru=await fetch(computerUrl(Au,lo),{...$u,headers:{...$u.body?{"Content-Type":"application/json"}:{},...Au.token?{Authorization:`Bearer ${Au.token}`}:{}}});if(!Ru.ok){let Iu=`${Ru.status}`;try{const Pu=await Ru.json();Iu=typeof Pu.detail=="string"?Pu.detail:Iu}catch{}throw new Error(Iu)}return Ru}const readComputer=async Au=>(await asked(Au,"")).json(),listComputerFiles=async(Au,lo=".")=>(await(await asked(Au,`/files?path=${encodeURIComponent(lo)}`)).json()).entries,downloadComputerFile=async(Au,lo)=>{const $u=await fetch(computerFileUrl(Au,lo),{headers:Au.token?{Authorization:`Bearer ${Au.token}`}:{}});if(!$u.ok){let Ru=`${$u.status}`;try{const Iu=await $u.json();Ru=typeof Iu.detail=="string"?Iu.detail:Ru}catch{}throw new Error(Ru)}return $u.blob()},takeOverComputer=async Au=>(await asked(Au,"/take-over",{method:"POST"})).json(),handBackComputer=async Au=>{await asked(Au,"/hand-back",{method:"POST"})},runOnComputer=async(Au,lo)=>(await asked(Au,"/run",{method:"POST",body:JSON.stringify({code:lo})})).json(),BROWSER_KEYS=["Enter","Tab","Backspace","Escape","ArrowUp","ArrowDown","ArrowLeft","ArrowRight","PageUp","PageDown"],readBrowserScreenshot=async Au=>{const lo=await fetch(computerUrl(Au,"/browser/screenshot"),{headers:Au.token?{Authorization:`Bearer ${Au.token}`}:{},cache:"no-store"});if(lo.status!==404){if(!lo.ok){let $u=`${lo.status}`;try{const Ru=await lo.json();$u=typeof Ru.detail=="string"?Ru.detail:$u}catch{}throw new Error($u)}return lo.blob()}},onBrowser=async(Au,lo,$u)=>(await(await asked(Au,`/browser/${lo}`,{method:"POST",body:JSON.stringify($u)})).json()).page,clickOnBrowser=(Au,lo,$u)=>onBrowser(Au,"click",{x:lo,y:$u}),typeOnBrowser=(Au,lo)=>onBrowser(Au,"type",{text:lo}),pressOnBrowser=(Au,lo)=>onBrowser(Au,"key",{key:lo}),scrollBrowser=(Au,lo)=>onBrowser(Au,"scroll",{dy:lo}),openOnBrowser=(Au,lo)=>onBrowser(Au,"open",{url:lo});function pointOnPage(Au,lo,$u){const Ru=(Iu,Pu,Ou)=>Pu>0?Math.round(Iu*Ou/Pu):Iu;return{x:Math.max(0,Math.min($u.width,Ru(Au.x,lo.width,$u.width))),y:Math.max(0,Math.min($u.height,Ru(Au.y,lo.height,$u.height)))}}function parentOf(Au){const lo=Au.split("/").filter($u=>$u&&$u!==".");return lo.pop(),lo.length?lo.join("/"):"."}const NO_TURN$2=signal$2({id:0,status:"idle"}),NO_CONVERSATION$1=signal$2([]),MONO_FACE='ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace',MONO={fontFamily:MONO_FACE,fontSize:0,whiteSpace:"pre-wrap",wordBreak:"break-word",m:0};function Parts({app:Au}){const lo=computerParts(Au),$u=partsOff(lo);return jsxRuntimeExports.jsxs(Box,{"data-testid":"computer-parts",children:[jsxRuntimeExports.jsx(Box,{display:"flex",gap:1,flexWrap:"wrap",mb:1,children:COMPUTER_PARTS.map(Ru=>jsxRuntimeExports.jsxs(Label,{variant:lo[Ru]?"success":"secondary","data-part":Ru,"data-on":lo[Ru]?"true":"false",children:[COMPUTER_WORDS.part[Ru],":"," ",lo[Ru]?COMPUTER_WORDS.on:COMPUTER_WORDS.off]},Ru))}),$u.length>0&&$u.length<COMPUTER_PARTS.length?jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:0,color:"fg.muted",m:0},children:COMPUTER_WORDS.offSaid($u)}):null]})}function Terminal({conversation:Au}){const lo=terminalEntries(Au);return jsxRuntimeExports.jsxs(Box,{"data-testid":"computer-terminal",children:[jsxRuntimeExports.jsx(Heading$2,{as:"h4",sx:{fontSize:1,mt:3,mb:1},children:COMPUTER_WORDS.terminal}),lo.length===0?jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:1,color:"fg.muted",m:0},children:COMPUTER_WORDS.nothingRan}):jsxRuntimeExports.jsx(Box,{bg:"canvas.inset",border:"1px solid",borderColor:"border.muted",borderRadius:2,p:2,maxHeight:320,overflow:"auto",children:lo.map(($u,Ru)=>jsxRuntimeExports.jsxs(Box,{mb:2,children:[jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{...MONO,fontWeight:"semibold"},children:`$ ${$u.tool}${$u.command?`: ${$u.command}`:""}`}),$u.output===void 0?jsxRuntimeExports.jsx(StyledSpinner,{size:"small"}):jsxRuntimeExports.jsx(Text$3,{as:"pre",sx:{...MONO,color:"fg.muted"},children:$u.output})]},Ru))})]})}function save(Au,lo){const $u=URL.createObjectURL(new Blob([Au],{type:"application/octet-stream"})),Ru=document.createElement("a");Ru.href=$u,Ru.download=lo,Ru.click(),URL.revokeObjectURL($u)}function Files({context:Au,state:lo,ended:$u}){const[Ru,Iu]=reactExports.useState("."),[Pu,Ou]=reactExports.useState(),[Mu,Du]=reactExports.useState("");return reactExports.useEffect(()=>{if(!lo.started)return;let Lu=!1;return listComputerFiles(Au,Ru).then(Bu=>{Lu||(Ou(Bu),Du(""))},Bu=>!Lu&&Du(Bu instanceof Error?Bu.message:String(Bu))),()=>{Lu=!0}},[Au.serverUrl,Au.agentId,Au.token,Ru,lo.started,$u]),jsxRuntimeExports.jsxs(Box,{"data-testid":"computer-files",children:[jsxRuntimeExports.jsxs(Heading$2,{as:"h4",sx:{fontSize:1,mt:3,mb:1},children:[COMPUTER_WORDS.files,Ru!=="."?jsxRuntimeExports.jsx(Text$3,{sx:{fontWeight:"normal",color:"fg.muted"},children:` · ${Ru}`}):null]}),lo.started?Mu?jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:1,color:"danger.fg",m:0},children:Mu}):Pu?jsxRuntimeExports.jsxs(Box,{as:"ul",listStyle:"none",p:0,m:0,children:[Ru!=="."?jsxRuntimeExports.jsx(Box,{as:"li",children:jsxRuntimeExports.jsx(ButtonComponent,{size:"small",variant:"invisible",onClick:()=>Iu(parentOf(Ru)),children:COMPUTER_WORDS.up})}):null,Pu.length===0?jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:1,color:"fg.muted",m:0},children:COMPUTER_WORDS.emptyDirectory}):null,Pu.map(Lu=>jsxRuntimeExports.jsx(Box,{as:"li","data-path":Lu.path,display:"flex",alignItems:"center",gap:2,fontSize:1,py:1,borderTop:"1px solid",borderColor:"border.muted",children:Lu.type==="directory"?jsxRuntimeExports.jsx(ButtonComponent,{size:"small",variant:"invisible",onClick:()=>Iu(Lu.path),children:`${Lu.name}/`}):jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(Text$3,{sx:{flex:1,fontFamily:MONO_FACE,fontSize:0},children:Lu.name}),jsxRuntimeExports.jsx(Text$3,{sx:{color:"fg.muted",fontSize:0},children:`${Lu.size.toLocaleString("en")} bytes`}),jsxRuntimeExports.jsx(ButtonComponent,{size:"small","aria-label":`${COMPUTER_WORDS.download} ${Lu.name}`,onClick:()=>{downloadComputerFile(Au,Lu.path).then(Bu=>save(Bu,Lu.name),Bu=>Du(Bu instanceof Error?Bu.message:String(Bu)))},children:COMPUTER_WORDS.download})]})},Lu.path))]}):jsxRuntimeExports.jsx(StyledSpinner,{size:"small"}):jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:1,color:"fg.muted",m:0},children:COMPUTER_WORDS.notStarted})]})}const BROWSER_POLL_MS=1500;function BrowserView({context:Au,state:lo,mine:$u}){const[Ru,Iu]=reactExports.useState(),[Pu,Ou]=reactExports.useState(!1),[Mu,Du]=reactExports.useState(lo.page),[Lu,Bu]=reactExports.useState(""),[ju,Fu]=reactExports.useState(""),[zu,Uu]=reactExports.useState(""),[Hu,Vu]=reactExports.useState(0),Gu=reactExports.useRef(void 0);reactExports.useEffect(()=>Du(lo.page),[lo.page]),reactExports.useEffect(()=>{if(!lo.browser)return;let Yu=!1;const Xu=()=>readBrowserScreenshot(Au).then(td=>{Yu||(Bu(""),Ou(!td),Gu.current&&URL.revokeObjectURL(Gu.current),Gu.current=td?URL.createObjectURL(td):void 0,Iu(Gu.current))},td=>!Yu&&Bu(td instanceof Error?td.message:String(td)));Xu();const ed=setInterval(Xu,BROWSER_POLL_MS);return()=>{Yu=!0,clearInterval(ed)}},[Au.serverUrl,Au.agentId,Au.token,lo.browser,Hu]),reactExports.useEffect(()=>()=>{Gu.current&&URL.revokeObjectURL(Gu.current)},[]);const Wu=Yu=>Yu.then(Xu=>{Du(Xu),Bu(""),Vu(ed=>ed+1)},Xu=>Bu(Xu instanceof Error?Xu.message:String(Xu))),Ku=Yu=>{const Xu=Yu.currentTarget,ed=Xu.getBoundingClientRect(),td=pointOnPage({x:Yu.clientX-ed.left,y:Yu.clientY-ed.top},{width:ed.width,height:ed.height},{width:Xu.naturalWidth,height:Xu.naturalHeight});Xu.focus(),Wu(clickOnBrowser(Au,td.x,td.y))},Ju=Yu=>{Yu.metaKey||Yu.ctrlKey||Yu.altKey||(BROWSER_KEYS.includes(Yu.key)?(Yu.preventDefault(),Wu(pressOnBrowser(Au,Yu.key))):Yu.key.length===1&&(Yu.preventDefault(),Wu(typeOnBrowser(Au,Yu.key))))};return jsxRuntimeExports.jsxs(Box,{"data-testid":"computer-browser",children:[jsxRuntimeExports.jsxs(Heading$2,{as:"h4",sx:{fontSize:1,mt:3,mb:1},children:[COMPUTER_WORDS.browser,Mu?jsxRuntimeExports.jsx(Text$3,{sx:{fontWeight:"normal",color:"fg.muted"},children:` · ${Mu.title||Mu.url}`}):null]}),lo.browser?Pu||!Ru?jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:1,color:"fg.muted",m:0},children:Pu?COMPUTER_WORDS.noPage:null}):jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[Mu?jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontFamily:MONO_FACE,fontSize:0,color:"fg.muted",m:0,mb:1,wordBreak:"break-all"},children:Mu.url}):null,jsxRuntimeExports.jsx("img",{src:Ru,alt:COMPUTER_WORDS.pageAlt(Mu?.title||Mu?.url||""),"data-testid":"computer-browser-page",tabIndex:$u?0:-1,onClick:$u?Ku:void 0,onKeyDown:$u?Ju:void 0,style:{width:"100%",height:"auto",display:"block",border:"1px solid var(--borderColor-muted, #d0d7de)",borderRadius:6,cursor:$u?"pointer":"default"}})]}):jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:1,color:"fg.muted",m:0},children:COMPUTER_WORDS.noBrowser}),Lu?jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:1,color:"danger.fg",m:0,mt:1},children:Lu}):null,$u&&lo.browser?jsxRuntimeExports.jsxs(Box,{mt:2,"data-testid":"computer-browser-controls",children:[jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:0,color:"fg.muted",m:0,mb:1},children:COMPUTER_WORDS.clickToAct}),jsxRuntimeExports.jsxs(Box,{display:"flex",gap:1,mb:1,children:[jsxRuntimeExports.jsx(TextInput$1,{size:"small","aria-label":COMPUTER_WORDS.typeOnPage,placeholder:COMPUTER_WORDS.typeOnPage,value:ju,onChange:Yu=>Fu(Yu.target.value),sx:{flex:1}}),jsxRuntimeExports.jsx(ButtonComponent,{size:"small",disabled:!ju,onClick:()=>{Wu(typeOnBrowser(Au,ju)),Fu("")},children:COMPUTER_WORDS.type}),jsxRuntimeExports.jsx(ButtonComponent,{size:"small",onClick:()=>{Wu(pressOnBrowser(Au,"Enter"))},children:"Enter"})]}),jsxRuntimeExports.jsxs(Box,{display:"flex",gap:1,mb:1,children:[jsxRuntimeExports.jsx(TextInput$1,{size:"small","aria-label":COMPUTER_WORDS.address,placeholder:COMPUTER_WORDS.address,value:zu,onChange:Yu=>Uu(Yu.target.value),sx:{flex:1}}),jsxRuntimeExports.jsx(ButtonComponent,{size:"small",disabled:!zu.trim(),onClick:()=>{Wu(openOnBrowser(Au,zu.trim()))},children:COMPUTER_WORDS.open})]}),jsxRuntimeExports.jsxs(Box,{display:"flex",gap:1,children:[jsxRuntimeExports.jsx(ButtonComponent,{size:"small",onClick:()=>{Wu(scrollBrowser(Au,-400))},children:COMPUTER_WORDS.scrollUp}),jsxRuntimeExports.jsx(ButtonComponent,{size:"small",onClick:()=>{Wu(scrollBrowser(Au,400))},children:COMPUTER_WORDS.scrollDown})]})]}):null]})}function YourTerminal({context:Au}){const[lo,$u]=reactExports.useState(""),[Ru,Iu]=reactExports.useState(!1),[Pu,Ou]=reactExports.useState([]),Mu=()=>{Iu(!0),runOnComputer(Au,lo).then(Du=>{Ou(Lu=>[...Lu,{code:lo,output:Du}]),$u(""),Iu(!1)},Du=>{Ou(Lu=>[...Lu,{code:lo,output:{stdout:"",stderr:"",error:Du instanceof Error?Du.message:String(Du)}}]),Iu(!1)})};return jsxRuntimeExports.jsxs(Box,{"data-testid":"computer-your-terminal",mt:2,children:[Pu.map((Du,Lu)=>jsxRuntimeExports.jsxs(Box,{mb:2,children:[jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{...MONO,fontWeight:"semibold"},children:`>>> ${Du.code}`}),jsxRuntimeExports.jsx(Text$3,{as:"pre",sx:{...MONO,color:"fg.muted"},children:[Du.output.stdout,Du.output.stderr,Du.output.error].filter(Boolean).join(`
|
|
4019
|
-
`)})]},Lu)),jsxRuntimeExports.jsx(Textarea,{"aria-label":COMPUTER_WORDS.yourCode,placeholder:COMPUTER_WORDS.yourCode,value:lo,onChange:Du=>$u(Du.target.value),sx:{width:"100%",fontFamily:MONO_FACE},rows:3}),jsxRuntimeExports.jsx(ButtonComponent,{size:"small",sx:{mt:1},disabled:Ru||!lo.trim(),onClick:Mu,children:COMPUTER_WORDS.run})]})}function AppComputer({app:Au,workspace:lo}){const $u=useIAMStore(sd=>sd.token)??"",Ru=useContributions(LoopChatTurn),Iu=useSignalValue$1(Ru[0]?.value.turn??NO_TURN$2),Pu=useSignalValue$1(Ru[0]?.value.conversation??NO_CONVERSATION$1),Ou=Iu.status!=="thinking"&&Iu.status!=="streaming",[Mu,Du]=reactExports.useState(0);reactExports.useEffect(()=>{Ou&&Du(Iu.id)},[Ou,Iu.id]);const Lu=useChatWords(),Bu=lo?agentServerOf(lo.sandbox,lo.serverUrl):void 0,ju=Bu===void 0?noRuntimeSaid(lo?.sandbox??IDLE_SANDBOX_SNAPSHOT,Lu):void 0,Fu=lo?.agentId||Au.id,[zu,Uu]=reactExports.useState(),Hu=zu?.servedFrom??"",Vu=reactExports.useMemo(()=>Bu===void 0?void 0:{serverUrl:Bu,agentId:Fu,token:$u||void 0,userAppsUrl:Hu||void 0},[Bu,Fu,$u,Hu]),Gu=computerParts(Au),[Wu,Ku]=reactExports.useState(""),[Ju,Yu]=reactExports.useState(!1),Xu=reactExports.useRef(!0);reactExports.useEffect(()=>(Xu.current=!0,()=>{Xu.current=!1}),[]);const ed=reactExports.useCallback(()=>{Vu&&readComputer(Vu).then(sd=>{Xu.current&&(Uu(sd),Ku(""))},sd=>Xu.current&&Ku(sd instanceof Error?sd.message:String(sd)))},[Vu]),td=hasComputer(Gu);reactExports.useEffect(()=>{td&&Vu&&ed()},[td,Vu,ed,Mu]);const nd=sd=>{Vu&&(Yu(!0),sd(Vu).then(()=>{Xu.current&&(Yu(!1),ed())},ad=>{Xu.current&&(Yu(!1),Ku(ad instanceof Error?ad.message:String(ad)))}))},od=!!zu?.yours;return jsxRuntimeExports.jsxs(Box,{"data-testid":"app-computer",children:[jsxRuntimeExports.jsx(Heading$2,{as:"h3",sx:{fontSize:2,mb:2},children:COMPUTER_WORDS.title}),jsxRuntimeExports.jsx(Parts,{app:Au}),td?jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[ju?jsxRuntimeExports.jsx(Text$3,{as:"p",role:"status",sx:{fontSize:1,color:"fg.default",m:0,mt:2},children:ju}):null,Wu?jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:1,color:"danger.fg",m:0,mt:2},children:Wu}):null,zu&&Vu?jsxRuntimeExports.jsxs(Box,{mt:2,"data-testid":"computer-holder",children:[jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:1,m:0,mb:1},children:zu.held?od?COMPUTER_WORDS.youHave:COMPUTER_WORDS.someoneHas:COMPUTER_WORDS.agentHas}),zu.held?od?jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(ButtonComponent,{size:"small",variant:"primary",disabled:Ju,onClick:()=>nd(handBackComputer),children:COMPUTER_WORDS.handBack}),jsxRuntimeExports.jsx(YourTerminal,{context:Vu})]}):null:jsxRuntimeExports.jsx(ButtonComponent,{size:"small",disabled:Ju,onClick:()=>nd(takeOverComputer),children:COMPUTER_WORDS.takeOver})]}):null,Gu.browse&&zu&&Vu?jsxRuntimeExports.jsx(BrowserView,{context:Vu,state:zu,mine:od}):null,jsxRuntimeExports.jsx(Terminal,{conversation:Pu}),zu&&Vu?jsxRuntimeExports.jsx(Files,{context:Vu,state:zu,ended:Mu}):null]}):jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:1,color:"fg.muted",m:0,mt:1},children:COMPUTER_WORDS.none})]})}const APP_COMPUTER_PLUGIN_NAME="@datalayer/loop-plugin-app-computer";function defineAppComputerPlugin(Au){function lo({workspace:Ru}){return jsxRuntimeExports.jsx(AppComputer,{app:Au,workspace:Ru})}return{name:`${APP_COMPUTER_PLUGIN_NAME}-${Au.id}`,displayName:`${Au.name}: computer`,description:`The computer ${Au.name} runs on: what ran on it, its files, and taking it over.`,octicon:"device-desktop",build:()=>({components:[{id:"app-computer",slot:LoopSlots.sidebar,order:30,Component:lo,rail:{label:"Computer",icon:DeviceDesktopIcon}}]})}}const SESSION_RULE_DECISIONS=["allow","ask","deny"],SESSION_RULE_CLASSES=["read","write","send","buy","delete","execute"],SESSION_RULES_WORDS={title:"This session",none:"No rule of your own on this session yet.",noSession:"Start a conversation to add a rule to its session.",add:"Add",remove:"Remove",namePlaceholder:"A name, quoted in every sentence it decides",decisions:{allow:"do it",ask:"ask me first",deny:"leave it to me"},classes:{read:"reading",write:"writing",send:"sending",buy:"buying",delete:"deleting",execute:"running a command"},scopes:{organization:"your organization",team:"this application",session:"this session"},goneWithSession:"In force at once, for this session only; gone when it ends.",unavailable:Au=>`Nothing is decided right now: ${Au}`};function sessionRuleInWords(Au){const lo=(Au.classes??[]).map(Ru=>SESSION_RULES_WORDS.classes[Ru]??Ru);return`${lo.length>0?lo.join(", "):"everything"}: ${SESSION_RULES_WORDS.decisions[Au.decision]}`}function sessionRuleOf(Au){const lo=Au.name.trim();if(!lo)return"A rule needs a name: it is quoted in every sentence it decides.";if(!SESSION_RULE_DECISIONS.includes(Au.decision))return`A rule says ${SESSION_RULE_DECISIONS.join(", ")}, not “${Au.decision}”.`;const $u=Au.classes.filter(Ru=>SESSION_RULE_CLASSES.includes(Ru));return{name:lo,decision:Au.decision,classes:$u}}const sessionRulesUrl=(Au,lo)=>`${Au}/api/v1/apps/sessions/${encodeURIComponent(lo)}/rules`;function parsed(Au){try{return Au?JSON.parse(Au):null}catch{return null}}async function answered(Au,lo){const $u=parsed(await Au.text());if(!Au.ok){const Ru=$u&&typeof $u=="object"&&"detail"in $u?String($u.detail):`${lo} (${Au.status})`;throw new Error(Ru)}return $u}async function readSessionRules(Au,lo,$u,Ru=fetch){const Iu=await Ru(sessionRulesUrl(Au,$u),{headers:{Authorization:`Bearer ${lo}`}});return answered(Iu,"The rules of the session could not be read")}async function addSessionRule(Au,lo,$u,Ru,Iu=fetch){const Pu=await Iu(sessionRulesUrl(Au,$u),{method:"POST",headers:{Authorization:`Bearer ${lo}`,"Content-Type":"application/json"},body:JSON.stringify(Ru)});return answered(Pu,"The rule could not be added")}async function removeSessionRule(Au,lo,$u,Ru,Iu=fetch){const Pu=await Iu(`${sessionRulesUrl(Au,$u)}/${encodeURIComponent(Ru)}`,{method:"DELETE",headers:{Authorization:`Bearer ${lo}`}});return answered(Pu,"The rule could not be removed")}const SAVE_TOOL="save_to_space",SAVE_WORDS={approve:"Approve and save",decline:"Decline",where:Au=>Au?`To keep as a page of ${Au}:`:"To keep as a page of its Space:"};function draftOfApproval(Au){if(Au.tool_name!==SAVE_TOOL)return null;const lo=$u=>{const Ru=Au.tool_args?.[$u];return typeof Ru=="string"?Ru:""};return{title:lo("title"),content:lo("content"),space:lo("space")}}const APP_RULES_WORDS={title:"Rules and approvals",byDefault:"With no rule of its own: reading is done, anything else waits for you.",none:"It has no rule of its own.",waiting:"Waiting for you",nothingWaiting:"Nothing is waiting for your yes.",signedOut:"Its approvals reach you here once you are signed in to Datalayer.",offline:"Approvals are not connected yet: what you approve or reject is not sent until they are.",approve:"Approve",reject:"Reject"};function ruleInWords(Au){const lo=BEHAVIOUR_WORDS[Au.behaviour];return{action:Au.action,covers:Au.appliesTo.map(coverOf).join(", "),says:lo.says,means:lo.means}}const ruleOfApproval=Au=>{const lo=Au.tool_args?._rule??Au.tool_args?._check;return typeof lo=="string"?lo:""};function approvalsOfApp(Au,lo){return Au.filter($u=>$u.agent_id===lo||$u.tool_args?._app===lo)}const PENDING={status:"pending"};function DraftShown({draft:Au}){return jsxRuntimeExports.jsxs(Box,{"data-testid":"app-draft",mt:1,children:[jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{m:0,fontSize:0,color:"fg.muted"},children:SAVE_WORDS.where(Au.space)}),jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{m:0,fontWeight:"semibold"},children:Au.title}),jsxRuntimeExports.jsx(Box,{as:"pre",m:0,mt:1,p:2,maxHeight:240,overflow:"auto",whiteSpace:"pre-wrap",fontFamily:"inherit",fontSize:0,bg:"canvas.subtle",borderRadius:2,children:Au.content})]})}function Approvals({app:Au}){const lo=useToolApprovalsQuery(PENDING),$u=useApproveToolRequest(),Ru=useRejectToolRequest(),Iu=reactExports.useMemo(()=>approvalsOfApp(lo.data?.approvals??[],Au.id),[lo.data,Au.id]);return jsxRuntimeExports.jsxs(Box,{"data-testid":"app-approvals",children:[jsxRuntimeExports.jsx(Heading$2,{as:"h4",sx:{fontSize:1,mt:3,mb:1},children:APP_RULES_WORDS.waiting}),$u.connectionState!=="connected"?jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:0,color:"fg.muted",m:0,mb:1},children:APP_RULES_WORDS.offline}):null,Iu.length===0?jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:1,color:"fg.muted",m:0},children:APP_RULES_WORDS.nothingWaiting}):jsxRuntimeExports.jsx(Box,{as:"ul",listStyle:"none",p:0,m:0,children:Iu.map(Pu=>{const Ou=draftOfApproval(Pu);return jsxRuntimeExports.jsxs(Box,{as:"li",py:2,borderTop:"1px solid",borderColor:"border.muted",fontSize:1,children:[jsxRuntimeExports.jsx(Text$3,{sx:{fontWeight:"semibold"},children:Pu.tool_name}),ruleOfApproval(Pu)?jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{m:0,color:"fg.muted",fontSize:0},children:ruleOfApproval(Pu)}):null,Ou?jsxRuntimeExports.jsx(DraftShown,{draft:Ou}):null,jsxRuntimeExports.jsxs(Box,{display:"flex",gap:2,mt:1,children:[jsxRuntimeExports.jsx(ButtonComponent,{size:"small",variant:"primary",disabled:$u.isPending,onClick:()=>$u.mutate({id:Pu.id}),children:Ou?SAVE_WORDS.approve:APP_RULES_WORDS.approve}),jsxRuntimeExports.jsx(ButtonComponent,{size:"small",disabled:Ru.isPending,onClick:()=>Ru.mutate({id:Pu.id}),children:Ou?SAVE_WORDS.decline:APP_RULES_WORDS.reject})]})]},Pu.id)})})]})}function SessionRules({agentServerUrl:Au,sessionUid:lo,token:$u}){const[Ru,Iu]=reactExports.useState(null),[Pu,Ou]=reactExports.useState(""),[Mu,Du]=reactExports.useState(""),[Lu,Bu]=reactExports.useState("ask"),[ju,Fu]=reactExports.useState("send"),[zu,Uu]=reactExports.useState(!1),Hu=reactExports.useCallback(()=>{let Wu=!1;return readSessionRules(Au,$u,lo).then(Ku=>!Wu&&Iu(Ku),Ku=>!Wu&&Ou(Ku instanceof Error?Ku.message:String(Ku))),()=>{Wu=!0}},[Au,$u,lo]);reactExports.useEffect(Hu,[Hu]);const Vu=async Wu=>{Uu(!0),Ou("");try{Iu(await Wu())}catch(Ku){Ou(Ku instanceof Error?Ku.message:String(Ku))}finally{Uu(!1)}},Gu=()=>{const Wu=sessionRuleOf({name:Mu,decision:Lu,classes:[ju]});if(typeof Wu=="string"){Ou(Wu);return}Vu(()=>addSessionRule(Au,$u,lo,Wu)).then(()=>Du(""))};return jsxRuntimeExports.jsxs(Box,{"data-testid":"session-rules",mt:3,children:[jsxRuntimeExports.jsx(Heading$2,{as:"h4",sx:{fontSize:1,mb:1},children:SESSION_RULES_WORDS.title}),Ru?.unavailable?jsxRuntimeExports.jsx(Text$3,{as:"p",role:"alert",sx:{fontSize:0,m:0,mb:1},children:SESSION_RULES_WORDS.unavailable(Ru.unavailable)}):null,Ru&&Ru.session_rules.length===0?jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:1,color:"fg.muted",m:0},children:SESSION_RULES_WORDS.none}):jsxRuntimeExports.jsx(Box,{as:"ul",listStyle:"none",p:0,m:0,children:(Ru?.session_rules??[]).map(Wu=>jsxRuntimeExports.jsxs(Box,{as:"li",py:1,fontSize:1,display:"flex",gap:2,alignItems:"center",children:[jsxRuntimeExports.jsx(Text$3,{sx:{fontWeight:"semibold"},children:Wu.name}),jsxRuntimeExports.jsx(Text$3,{sx:{color:"fg.muted",fontSize:0},children:sessionRuleInWords(Wu)}),jsxRuntimeExports.jsx(ButtonComponent,{size:"small",variant:"invisible",disabled:zu,onClick:()=>{Vu(()=>removeSessionRule(Au,$u,lo,Wu.name))},children:SESSION_RULES_WORDS.remove})]},Wu.name))}),jsxRuntimeExports.jsxs(Box,{display:"flex",gap:1,mt:1,flexWrap:"wrap",alignItems:"center",children:[jsxRuntimeExports.jsx(TextInput$1,{size:"small",value:Mu,placeholder:SESSION_RULES_WORDS.namePlaceholder,"aria-label":"The rule's name",onChange:Wu=>Du(Wu.target.value)}),jsxRuntimeExports.jsx(Select$1,{size:"small",value:ju,"aria-label":"What it covers",onChange:Wu=>Fu(Wu.target.value),children:SESSION_RULE_CLASSES.map(Wu=>jsxRuntimeExports.jsx(Select$1.Option,{value:Wu,children:SESSION_RULES_WORDS.classes[Wu]},Wu))}),jsxRuntimeExports.jsx(Select$1,{size:"small",value:Lu,"aria-label":"What it says",onChange:Wu=>Bu(Wu.target.value),children:SESSION_RULE_DECISIONS.map(Wu=>jsxRuntimeExports.jsx(Select$1.Option,{value:Wu,children:SESSION_RULES_WORDS.decisions[Wu]},Wu))}),jsxRuntimeExports.jsx(ButtonComponent,{size:"small",disabled:zu,onClick:Gu,children:SESSION_RULES_WORDS.add})]}),Pu?jsxRuntimeExports.jsx(Text$3,{as:"p",role:"alert",sx:{fontSize:0,m:0,mt:1},children:Pu}):null,jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:0,color:"fg.muted",mt:1,mb:0},children:SESSION_RULES_WORDS.goneWithSession})]})}function AppRulesCard({app:Au,workspace:lo}){const $u=useIAMStore(Ou=>Ou.token)??"",Ru=!!$u,Iu=lo?agentServerOf(lo.sandbox,lo.serverUrl):void 0,Pu=lo?.conversationId;return jsxRuntimeExports.jsxs(Box,{"data-testid":"app-rules",mb:4,children:[jsxRuntimeExports.jsx(Heading$2,{as:"h3",sx:{fontSize:2,mb:2},children:APP_RULES_WORDS.title}),Au.rules.length===0?jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:1,color:"fg.muted",m:0},children:APP_RULES_WORDS.none}):jsxRuntimeExports.jsx(Box,{as:"ul",listStyle:"none",p:0,m:0,children:Au.rules.map(ruleInWords).map((Ou,Mu)=>jsxRuntimeExports.jsxs(Box,{as:"li",py:1,fontSize:1,children:[jsxRuntimeExports.jsx(Text$3,{children:Ou.action})," ",jsxRuntimeExports.jsx(Label,{title:Ou.means,children:Ou.says}),jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{m:0,color:"fg.muted",fontSize:0},children:Ou.covers})]},Mu))}),jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:0,color:"fg.muted",mt:1,mb:0},children:APP_RULES_WORDS.byDefault}),Ru?jsxRuntimeExports.jsx(Approvals,{app:Au}):jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:1,color:"fg.muted",mt:3,mb:0},children:APP_RULES_WORDS.signedOut}),Ru&&Iu?Pu?jsxRuntimeExports.jsx(SessionRules,{agentServerUrl:Iu,sessionUid:Pu,token:$u}):jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:0,color:"fg.muted",mt:3,mb:0},children:SESSION_RULES_WORDS.noSession}):null]})}const APP_RULES_PLUGIN_NAME="@datalayer/loop-plugin-app-rules";function defineAppRulesPlugin(Au){function lo(Ru){return jsxRuntimeExports.jsx(AppRulesCard,{app:Au,workspace:Ru.workspace})}return{name:`${APP_RULES_PLUGIN_NAME}-${Au.id}`,displayName:`${Au.name}: rules and approvals`,description:`When ${Au.name} acts alone, when it asks, and what waits for your yes.`,octicon:"shield-check",build:()=>({components:[{id:"app-rules",slot:LoopSlots.sidebar,order:10,Component:lo,rail:{label:"Rules",icon:ShieldCheckIcon}}]})}}const COMMAND_INPUT="{input}";function commandPrompt(Au,lo=""){const $u=lo.trim();return Au.prompt.includes(COMMAND_INPUT)?Au.prompt.split(COMMAND_INPUT).join($u).trim():$u?`${Au.prompt}
|
|
4019
|
+
`)})]},Lu)),jsxRuntimeExports.jsx(Textarea,{"aria-label":COMPUTER_WORDS.yourCode,placeholder:COMPUTER_WORDS.yourCode,value:lo,onChange:Du=>$u(Du.target.value),sx:{width:"100%",fontFamily:MONO_FACE},rows:3}),jsxRuntimeExports.jsx(ButtonComponent,{size:"small",sx:{mt:1},disabled:Ru||!lo.trim(),onClick:Mu,children:COMPUTER_WORDS.run})]})}function AppComputer({app:Au,workspace:lo}){const $u=useIAMStore(sd=>sd.token)??"",Ru=useContributions(LoopChatTurn),Iu=useSignalValue$1(Ru[0]?.value.turn??NO_TURN$2),Pu=useSignalValue$1(Ru[0]?.value.conversation??NO_CONVERSATION$1),Ou=Iu.status!=="thinking"&&Iu.status!=="streaming",[Mu,Du]=reactExports.useState(0);reactExports.useEffect(()=>{Ou&&Du(Iu.id)},[Ou,Iu.id]);const Lu=useChatWords(),Bu=lo?agentServerOf(lo.sandbox,lo.serverUrl):void 0,ju=Bu===void 0?noRuntimeSaid(lo?.sandbox??IDLE_SANDBOX_SNAPSHOT,Lu):void 0,Fu=lo?.agentId||Au.id,[zu,Uu]=reactExports.useState(),Hu=zu?.servedFrom??"",Vu=reactExports.useMemo(()=>Bu===void 0?void 0:{serverUrl:Bu,agentId:Fu,token:$u||void 0,userAppsUrl:Hu||void 0},[Bu,Fu,$u,Hu]),Gu=computerParts(Au),[Wu,Ku]=reactExports.useState(""),[Ju,Yu]=reactExports.useState(!1),Xu=reactExports.useRef(!0);reactExports.useEffect(()=>(Xu.current=!0,()=>{Xu.current=!1}),[]);const ed=reactExports.useCallback(()=>{Vu&&readComputer(Vu).then(sd=>{Xu.current&&(Uu(sd),Ku(""))},sd=>Xu.current&&Ku(sd instanceof Error?sd.message:String(sd)))},[Vu]),td=hasComputer(Gu);reactExports.useEffect(()=>{td&&Vu&&ed()},[td,Vu,ed,Mu]);const nd=sd=>{Vu&&(Yu(!0),sd(Vu).then(()=>{Xu.current&&(Yu(!1),ed())},ad=>{Xu.current&&(Yu(!1),Ku(ad instanceof Error?ad.message:String(ad)))}))},od=!!zu?.yours;return jsxRuntimeExports.jsxs(Box,{"data-testid":"app-computer",children:[jsxRuntimeExports.jsx(Heading$2,{as:"h3",sx:{fontSize:2,mb:2},children:COMPUTER_WORDS.title}),jsxRuntimeExports.jsx(Parts,{app:Au}),td?jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[ju?jsxRuntimeExports.jsx(Text$3,{as:"p",role:"status",sx:{fontSize:1,color:"fg.default",m:0,mt:2},children:ju}):null,Wu?jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:1,color:"danger.fg",m:0,mt:2},children:Wu}):null,zu&&Vu?jsxRuntimeExports.jsxs(Box,{mt:2,"data-testid":"computer-holder",children:[jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:1,m:0,mb:1},children:zu.held?od?COMPUTER_WORDS.youHave:COMPUTER_WORDS.someoneHas:COMPUTER_WORDS.agentHas}),zu.held?od?jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment,{children:[jsxRuntimeExports.jsx(ButtonComponent,{size:"small",variant:"primary",disabled:Ju,onClick:()=>nd(handBackComputer),children:COMPUTER_WORDS.handBack}),jsxRuntimeExports.jsx(YourTerminal,{context:Vu})]}):null:jsxRuntimeExports.jsx(ButtonComponent,{size:"small",disabled:Ju,onClick:()=>nd(takeOverComputer),children:COMPUTER_WORDS.takeOver})]}):null,Gu.browse&&zu&&Vu?jsxRuntimeExports.jsx(BrowserView,{context:Vu,state:zu,mine:od}):null,jsxRuntimeExports.jsx(Terminal,{conversation:Pu}),zu&&Vu?jsxRuntimeExports.jsx(Files,{context:Vu,state:zu,ended:Mu}):null]}):jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:1,color:"fg.muted",m:0,mt:1},children:COMPUTER_WORDS.none})]})}const APP_COMPUTER_PLUGIN_NAME="@datalayer/loop-plugin-app-computer";function defineAppComputerPlugin(Au){function lo({workspace:Ru}){return jsxRuntimeExports.jsx(AppComputer,{app:Au,workspace:Ru})}return{name:`${APP_COMPUTER_PLUGIN_NAME}-${Au.id}`,displayName:`${Au.name}: computer`,description:`The computer ${Au.name} runs on: what ran on it, its files, and taking it over.`,octicon:"device-desktop",build:()=>({components:[{id:"app-computer",slot:LoopSlots.sidebar,order:30,Component:lo,rail:{label:"Computer",icon:DeviceDesktopIcon}}]})}}const SESSION_RULE_DECISIONS=["allow","ask","deny"],SESSION_RULE_CLASSES=["read","write","send","buy","delete","execute"],SESSION_RULES_WORDS={title:"This session",none:"No rule of your own on this session yet.",noSession:"Start a conversation to add a rule to its session.",add:"Add",remove:"Remove",namePlaceholder:"A name, quoted in every sentence it decides",decisions:{allow:"do it",ask:"ask me first",deny:"leave it to me"},classes:{read:"reading",write:"writing",send:"sending",buy:"buying",delete:"deleting",execute:"running a command"},scopes:{organization:"your organization",team:"this application",session:"this session"},goneWithSession:"In force at once, for this session only; gone when it ends.",unavailable:Au=>`Nothing is decided right now: ${Au}`},WHERE_WORDS={inside:"inside the workspace",outside:"outside the workspace"};function sessionRuleInWords(Au){const lo=(Au.classes??[]).map(Ru=>SESSION_RULES_WORDS.classes[Ru]??Ru);let $u=lo.length>0?lo.join(", "):"everything";return(Au.where==="inside"||Au.where==="outside")&&($u=`${$u} ${WHERE_WORDS[Au.where]}`),Au.paths&&Au.paths.length>0&&($u=`${$u} (${Au.paths.join(", ")})`),Au.commands&&Au.commands.length>0&&($u=`${$u} matching ${Au.commands.map(Ru=>`\`${Ru}\``).join(" or ")}`),`${$u}: ${SESSION_RULES_WORDS.decisions[Au.decision]}`}const FILE_AND_SHELL_RULES=[{name:"Edit inside the workspace",decision:"allow",classes:["write"],where:"inside"},{name:"Ask before deleting a file",decision:"ask",classes:["delete"]},{name:"Ask before running a command",decision:"ask",classes:["execute"]},{name:"Leave files outside the workspace to me",decision:"deny",classes:["read","write","delete"],where:"outside"},{name:"Ask before git push",decision:"ask",classes:["execute"],commands:["^git\\s+push\\b"]},{name:"Ask before installing a package",decision:"ask",classes:["execute"],commands:["\\b(pip|npm|uv|yarn|apt(-get)?)\\s+(install|add)\\b"]},{name:"Leave sudo to me",decision:"deny",classes:["execute"],commands:["^sudo\\b"]}];function sessionRuleOf(Au){const lo=Au.name.trim();if(!lo)return"A rule needs a name: it is quoted in every sentence it decides.";if(!SESSION_RULE_DECISIONS.includes(Au.decision))return`A rule says ${SESSION_RULE_DECISIONS.join(", ")}, not “${Au.decision}”.`;const $u=Au.classes.filter(Ru=>SESSION_RULE_CLASSES.includes(Ru));return{name:lo,decision:Au.decision,classes:$u}}const sessionRulesUrl=(Au,lo)=>`${Au}/api/v1/apps/sessions/${encodeURIComponent(lo)}/rules`;function parsed(Au){try{return Au?JSON.parse(Au):null}catch{return null}}async function answered(Au,lo){const $u=parsed(await Au.text());if(!Au.ok){const Ru=$u&&typeof $u=="object"&&"detail"in $u?String($u.detail):`${lo} (${Au.status})`;throw new Error(Ru)}return $u}async function readSessionRules(Au,lo,$u,Ru=fetch){const Iu=await Ru(sessionRulesUrl(Au,$u),{headers:{Authorization:`Bearer ${lo}`}});return answered(Iu,"The rules of the session could not be read")}async function addSessionRule(Au,lo,$u,Ru,Iu=fetch){const Pu=await Iu(sessionRulesUrl(Au,$u),{method:"POST",headers:{Authorization:`Bearer ${lo}`,"Content-Type":"application/json"},body:JSON.stringify(Ru)});return answered(Pu,"The rule could not be added")}async function removeSessionRule(Au,lo,$u,Ru,Iu=fetch){const Pu=await Iu(`${sessionRulesUrl(Au,$u)}/${encodeURIComponent(Ru)}`,{method:"DELETE",headers:{Authorization:`Bearer ${lo}`}});return answered(Pu,"The rule could not be removed")}const SAVE_TOOL="save_to_space",SAVE_WORDS={approve:"Approve and save",decline:"Decline",where:Au=>Au?`To keep as a page of ${Au}:`:"To keep as a page of its Space:"};function draftOfApproval(Au){if(Au.tool_name!==SAVE_TOOL)return null;const lo=$u=>{const Ru=Au.tool_args?.[$u];return typeof Ru=="string"?Ru:""};return{title:lo("title"),content:lo("content"),space:lo("space")}}const APP_RULES_WORDS={title:"Rules and approvals",byDefault:"With no rule of its own: reading is done, anything else waits for you.",none:"It has no rule of its own.",waiting:"Waiting for you",nothingWaiting:"Nothing is waiting for your yes.",signedOut:"Its approvals reach you here once you are signed in to Datalayer.",offline:"Approvals are not connected yet: what you approve or reject is not sent until they are.",approve:"Approve",reject:"Reject"};function ruleInWords(Au){const lo=BEHAVIOUR_WORDS[Au.behaviour];return{action:Au.action,covers:Au.appliesTo.map(coverOf).join(", "),says:lo.says,means:lo.means}}const ruleOfApproval=Au=>{const lo=Au.tool_args?._rule??Au.tool_args?._check;return typeof lo=="string"?lo:""};function approvalsOfApp(Au,lo){return Au.filter($u=>$u.agent_id===lo||$u.tool_args?._app===lo)}const PENDING={status:"pending"};function DraftShown({draft:Au}){return jsxRuntimeExports.jsxs(Box,{"data-testid":"app-draft",mt:1,children:[jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{m:0,fontSize:0,color:"fg.muted"},children:SAVE_WORDS.where(Au.space)}),jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{m:0,fontWeight:"semibold"},children:Au.title}),jsxRuntimeExports.jsx(Box,{as:"pre",m:0,mt:1,p:2,maxHeight:240,overflow:"auto",whiteSpace:"pre-wrap",fontFamily:"inherit",fontSize:0,bg:"canvas.subtle",borderRadius:2,children:Au.content})]})}function Approvals({app:Au}){const lo=useToolApprovalsQuery(PENDING),$u=useApproveToolRequest(),Ru=useRejectToolRequest(),Iu=reactExports.useMemo(()=>approvalsOfApp(lo.data?.approvals??[],Au.id),[lo.data,Au.id]);return jsxRuntimeExports.jsxs(Box,{"data-testid":"app-approvals",children:[jsxRuntimeExports.jsx(Heading$2,{as:"h4",sx:{fontSize:1,mt:3,mb:1},children:APP_RULES_WORDS.waiting}),$u.connectionState!=="connected"?jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:0,color:"fg.muted",m:0,mb:1},children:APP_RULES_WORDS.offline}):null,Iu.length===0?jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:1,color:"fg.muted",m:0},children:APP_RULES_WORDS.nothingWaiting}):jsxRuntimeExports.jsx(Box,{as:"ul",listStyle:"none",p:0,m:0,children:Iu.map(Pu=>{const Ou=draftOfApproval(Pu);return jsxRuntimeExports.jsxs(Box,{as:"li",py:2,borderTop:"1px solid",borderColor:"border.muted",fontSize:1,children:[jsxRuntimeExports.jsx(Text$3,{sx:{fontWeight:"semibold"},children:Pu.tool_name}),ruleOfApproval(Pu)?jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{m:0,color:"fg.muted",fontSize:0},children:ruleOfApproval(Pu)}):null,Ou?jsxRuntimeExports.jsx(DraftShown,{draft:Ou}):null,jsxRuntimeExports.jsxs(Box,{display:"flex",gap:2,mt:1,children:[jsxRuntimeExports.jsx(ButtonComponent,{size:"small",variant:"primary",disabled:$u.isPending,onClick:()=>$u.mutate({id:Pu.id}),children:Ou?SAVE_WORDS.approve:APP_RULES_WORDS.approve}),jsxRuntimeExports.jsx(ButtonComponent,{size:"small",disabled:Ru.isPending,onClick:()=>Ru.mutate({id:Pu.id}),children:Ou?SAVE_WORDS.decline:APP_RULES_WORDS.reject})]})]},Pu.id)})})]})}function SessionRules({agentServerUrl:Au,sessionUid:lo,token:$u}){const[Ru,Iu]=reactExports.useState(null),[Pu,Ou]=reactExports.useState(""),[Mu,Du]=reactExports.useState(""),[Lu,Bu]=reactExports.useState("ask"),[ju,Fu]=reactExports.useState("send"),[zu,Uu]=reactExports.useState(!1),Hu=reactExports.useCallback(()=>{let Wu=!1;return readSessionRules(Au,$u,lo).then(Ku=>!Wu&&Iu(Ku),Ku=>!Wu&&Ou(Ku instanceof Error?Ku.message:String(Ku))),()=>{Wu=!0}},[Au,$u,lo]);reactExports.useEffect(Hu,[Hu]);const Vu=async Wu=>{Uu(!0),Ou("");try{Iu(await Wu())}catch(Ku){Ou(Ku instanceof Error?Ku.message:String(Ku))}finally{Uu(!1)}},Gu=()=>{const Wu=sessionRuleOf({name:Mu,decision:Lu,classes:[ju]});if(typeof Wu=="string"){Ou(Wu);return}Vu(()=>addSessionRule(Au,$u,lo,Wu)).then(()=>Du(""))};return jsxRuntimeExports.jsxs(Box,{"data-testid":"session-rules",mt:3,children:[jsxRuntimeExports.jsx(Heading$2,{as:"h4",sx:{fontSize:1,mb:1},children:SESSION_RULES_WORDS.title}),Ru?.unavailable?jsxRuntimeExports.jsx(Text$3,{as:"p",role:"alert",sx:{fontSize:0,m:0,mb:1},children:SESSION_RULES_WORDS.unavailable(Ru.unavailable)}):null,Ru&&Ru.session_rules.length===0?jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:1,color:"fg.muted",m:0},children:SESSION_RULES_WORDS.none}):jsxRuntimeExports.jsx(Box,{as:"ul",listStyle:"none",p:0,m:0,children:(Ru?.session_rules??[]).map(Wu=>jsxRuntimeExports.jsxs(Box,{as:"li",py:1,fontSize:1,display:"flex",gap:2,alignItems:"center",children:[jsxRuntimeExports.jsx(Text$3,{sx:{fontWeight:"semibold"},children:Wu.name}),jsxRuntimeExports.jsx(Text$3,{sx:{color:"fg.muted",fontSize:0},children:sessionRuleInWords(Wu)}),jsxRuntimeExports.jsx(ButtonComponent,{size:"small",variant:"invisible",disabled:zu,onClick:()=>{Vu(()=>removeSessionRule(Au,$u,lo,Wu.name))},children:SESSION_RULES_WORDS.remove})]},Wu.name))}),jsxRuntimeExports.jsxs(Box,{display:"flex",gap:1,mt:1,flexWrap:"wrap",alignItems:"center",children:[jsxRuntimeExports.jsx(TextInput$1,{size:"small",value:Mu,placeholder:SESSION_RULES_WORDS.namePlaceholder,"aria-label":"The rule's name",onChange:Wu=>Du(Wu.target.value)}),jsxRuntimeExports.jsx(Select$1,{size:"small",value:ju,"aria-label":"What it covers",onChange:Wu=>Fu(Wu.target.value),children:SESSION_RULE_CLASSES.map(Wu=>jsxRuntimeExports.jsx(Select$1.Option,{value:Wu,children:SESSION_RULES_WORDS.classes[Wu]},Wu))}),jsxRuntimeExports.jsx(Select$1,{size:"small",value:Lu,"aria-label":"What it says",onChange:Wu=>Bu(Wu.target.value),children:SESSION_RULE_DECISIONS.map(Wu=>jsxRuntimeExports.jsx(Select$1.Option,{value:Wu,children:SESSION_RULES_WORDS.decisions[Wu]},Wu))}),jsxRuntimeExports.jsx(ButtonComponent,{size:"small",disabled:zu,onClick:Gu,children:SESSION_RULES_WORDS.add})]}),jsxRuntimeExports.jsx(Box,{display:"flex",gap:1,mt:1,flexWrap:"wrap","data-testid":"session-rules-file-and-shell",children:FILE_AND_SHELL_RULES.map(Wu=>jsxRuntimeExports.jsx(ButtonComponent,{size:"small",variant:"invisible",disabled:zu,title:sessionRuleInWords(Wu),onClick:()=>{Vu(()=>addSessionRule(Au,$u,lo,Wu))},children:Wu.name},Wu.name))}),Pu?jsxRuntimeExports.jsx(Text$3,{as:"p",role:"alert",sx:{fontSize:0,m:0,mt:1},children:Pu}):null,jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:0,color:"fg.muted",mt:1,mb:0},children:SESSION_RULES_WORDS.goneWithSession})]})}function AppRulesCard({app:Au,workspace:lo}){const $u=useIAMStore(Ou=>Ou.token)??"",Ru=!!$u,Iu=lo?agentServerOf(lo.sandbox,lo.serverUrl):void 0,Pu=lo?.conversationId;return jsxRuntimeExports.jsxs(Box,{"data-testid":"app-rules",mb:4,children:[jsxRuntimeExports.jsx(Heading$2,{as:"h3",sx:{fontSize:2,mb:2},children:APP_RULES_WORDS.title}),Au.rules.length===0?jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:1,color:"fg.muted",m:0},children:APP_RULES_WORDS.none}):jsxRuntimeExports.jsx(Box,{as:"ul",listStyle:"none",p:0,m:0,children:Au.rules.map(ruleInWords).map((Ou,Mu)=>jsxRuntimeExports.jsxs(Box,{as:"li",py:1,fontSize:1,children:[jsxRuntimeExports.jsx(Text$3,{children:Ou.action})," ",jsxRuntimeExports.jsx(Label,{title:Ou.means,children:Ou.says}),jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{m:0,color:"fg.muted",fontSize:0},children:Ou.covers})]},Mu))}),jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:0,color:"fg.muted",mt:1,mb:0},children:APP_RULES_WORDS.byDefault}),Ru?jsxRuntimeExports.jsx(Approvals,{app:Au}):jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:1,color:"fg.muted",mt:3,mb:0},children:APP_RULES_WORDS.signedOut}),Ru&&Iu?Pu?jsxRuntimeExports.jsx(SessionRules,{agentServerUrl:Iu,sessionUid:Pu,token:$u}):jsxRuntimeExports.jsx(Text$3,{as:"p",sx:{fontSize:0,color:"fg.muted",mt:3,mb:0},children:SESSION_RULES_WORDS.noSession}):null]})}const APP_RULES_PLUGIN_NAME="@datalayer/loop-plugin-app-rules";function defineAppRulesPlugin(Au){function lo(Ru){return jsxRuntimeExports.jsx(AppRulesCard,{app:Au,workspace:Ru.workspace})}return{name:`${APP_RULES_PLUGIN_NAME}-${Au.id}`,displayName:`${Au.name}: rules and approvals`,description:`When ${Au.name} acts alone, when it asks, and what waits for your yes.`,octicon:"shield-check",build:()=>({components:[{id:"app-rules",slot:LoopSlots.sidebar,order:10,Component:lo,rail:{label:"Rules",icon:ShieldCheckIcon}}]})}}const COMMAND_INPUT="{input}";function commandPrompt(Au,lo=""){const $u=lo.trim();return Au.prompt.includes(COMMAND_INPUT)?Au.prompt.split(COMMAND_INPUT).join($u).trim():$u?`${Au.prompt}
|
|
4020
4020
|
|
|
4021
4021
|
${$u}`:Au.prompt}function modeDefaults(Au){return Object.fromEntries((Au.interface.modes??[]).filter(lo=>lo.options.length>0).map(lo=>[lo.id,lo.default||lo.options[0].id]))}function modeChoice(Au,lo={}){const $u=Au.interface.modes??[];for(const[Ru,Iu]of Object.entries(lo)){const Pu=$u.find(Ou=>Ou.id===Ru);if(!Pu)throw new Error(`${Au.name} has no mode “${Ru}”.`);if(!Pu.options.some(Ou=>Ou.id===Iu))throw new Error(`The mode ${Pu.label} has no option “${Iu}”.`)}return{...modeDefaults(Au),...lo}}function profileChoice(Au,lo){const $u=Au.interface.profiles??[];if(lo){const Ru=$u.find(Iu=>Iu.id===lo);if(!Ru)throw new Error($u.length===0?`${Au.name} has no profiles: “${lo}” cannot be chosen.`:`${Au.name} has no profile “${lo}”.`);return Ru}return $u[0]}function startersFor(Au,lo){const $u=profileChoice(Au,lo);return $u&&$u.starters.length>0?$u.starters:Au.interface.starters}function modeEffect(Au,lo={},$u){const Ru=modeChoice(Au,lo),Iu=profileChoice(Au,$u),Pu=(Au.interface.modes??[]).flatMap(Du=>Du.options.filter(Lu=>Lu.id===Ru[Du.id])),Ou=[Iu?.instructions??"",...Pu.map(Du=>Du.instructions??"")].map(Du=>Du.trim()).filter(Boolean).join(`
|
|
4022
4022
|
|
|
@@ -61,8 +61,21 @@ export declare const SESSION_RULES_WORDS: {
|
|
|
61
61
|
readonly goneWithSession: "In force at once, for this session only; gone when it ends.";
|
|
62
62
|
readonly unavailable: (sentence: string) => string;
|
|
63
63
|
};
|
|
64
|
-
/**
|
|
64
|
+
/** Where a file rule holds, in words (SHIELD decision 5). */
|
|
65
|
+
export declare const WHERE_WORDS: Record<'inside' | 'outside', string>;
|
|
66
|
+
/**
|
|
67
|
+
* A rule in words: *writing, sending: leave it to me*; with decision 5's file
|
|
68
|
+
* and shell wording when it says where or what (SHIELD SH-43) — *writing
|
|
69
|
+
* inside the workspace (src/**): do it*, *running `git push`: ask me first*.
|
|
70
|
+
*/
|
|
65
71
|
export declare function sessionRuleInWords(rule: SessionRule): string;
|
|
72
|
+
/**
|
|
73
|
+
* Decision 5's file and shell rules, offered on a running session in the
|
|
74
|
+
* Shield's own words: each one a rule a person adds as it stands, and the
|
|
75
|
+
* runtime refuses one that would loosen what the organization says
|
|
76
|
+
* (SHIELD §6.2, §6.3).
|
|
77
|
+
*/
|
|
78
|
+
export declare const FILE_AND_SHELL_RULES: readonly SessionRule[];
|
|
66
79
|
/** What the person typed, as the rule the runtime takes; a sentence when it cannot be one. */
|
|
67
80
|
export declare function sessionRuleOf(input: {
|
|
68
81
|
name: string;
|
|
@@ -54,12 +54,78 @@ export const SESSION_RULES_WORDS = {
|
|
|
54
54
|
goneWithSession: 'In force at once, for this session only; gone when it ends.',
|
|
55
55
|
unavailable: (sentence) => `Nothing is decided right now: ${sentence}`,
|
|
56
56
|
};
|
|
57
|
-
/**
|
|
57
|
+
/** Where a file rule holds, in words (SHIELD decision 5). */
|
|
58
|
+
export const WHERE_WORDS = {
|
|
59
|
+
inside: 'inside the workspace',
|
|
60
|
+
outside: 'outside the workspace',
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* A rule in words: *writing, sending: leave it to me*; with decision 5's file
|
|
64
|
+
* and shell wording when it says where or what (SHIELD SH-43) — *writing
|
|
65
|
+
* inside the workspace (src/**): do it*, *running `git push`: ask me first*.
|
|
66
|
+
*/
|
|
58
67
|
export function sessionRuleInWords(rule) {
|
|
59
68
|
const classes = (rule.classes ?? []).map(item => SESSION_RULES_WORDS.classes[item] ?? item);
|
|
60
|
-
|
|
69
|
+
let covers = classes.length > 0 ? classes.join(', ') : 'everything';
|
|
70
|
+
if (rule.where === 'inside' || rule.where === 'outside') {
|
|
71
|
+
covers = `${covers} ${WHERE_WORDS[rule.where]}`;
|
|
72
|
+
}
|
|
73
|
+
if (rule.paths && rule.paths.length > 0) {
|
|
74
|
+
covers = `${covers} (${rule.paths.join(', ')})`;
|
|
75
|
+
}
|
|
76
|
+
if (rule.commands && rule.commands.length > 0) {
|
|
77
|
+
covers = `${covers} matching ${rule.commands.map(command => `\`${command}\``).join(' or ')}`;
|
|
78
|
+
}
|
|
61
79
|
return `${covers}: ${SESSION_RULES_WORDS.decisions[rule.decision]}`;
|
|
62
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* Decision 5's file and shell rules, offered on a running session in the
|
|
83
|
+
* Shield's own words: each one a rule a person adds as it stands, and the
|
|
84
|
+
* runtime refuses one that would loosen what the organization says
|
|
85
|
+
* (SHIELD §6.2, §6.3).
|
|
86
|
+
*/
|
|
87
|
+
export const FILE_AND_SHELL_RULES = [
|
|
88
|
+
{
|
|
89
|
+
name: 'Edit inside the workspace',
|
|
90
|
+
decision: 'allow',
|
|
91
|
+
classes: ['write'],
|
|
92
|
+
where: 'inside',
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: 'Ask before deleting a file',
|
|
96
|
+
decision: 'ask',
|
|
97
|
+
classes: ['delete'],
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
name: 'Ask before running a command',
|
|
101
|
+
decision: 'ask',
|
|
102
|
+
classes: ['execute'],
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: 'Leave files outside the workspace to me',
|
|
106
|
+
decision: 'deny',
|
|
107
|
+
classes: ['read', 'write', 'delete'],
|
|
108
|
+
where: 'outside',
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: 'Ask before git push',
|
|
112
|
+
decision: 'ask',
|
|
113
|
+
classes: ['execute'],
|
|
114
|
+
commands: ['^git\\s+push\\b'],
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: 'Ask before installing a package',
|
|
118
|
+
decision: 'ask',
|
|
119
|
+
classes: ['execute'],
|
|
120
|
+
commands: ['\\b(pip|npm|uv|yarn|apt(-get)?)\\s+(install|add)\\b'],
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: 'Leave sudo to me',
|
|
124
|
+
decision: 'deny',
|
|
125
|
+
classes: ['execute'],
|
|
126
|
+
commands: ['^sudo\\b'],
|
|
127
|
+
},
|
|
128
|
+
];
|
|
63
129
|
/** What the person typed, as the rule the runtime takes; a sentence when it cannot be one. */
|
|
64
130
|
export function sessionRuleOf(input) {
|
|
65
131
|
const name = input.name.trim();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** The kinds of part an agent is built from on the Canvas. */
|
|
2
|
-
export type CanvasPartKind = 'connection' | 'rule' | 'skill' | 'test' | 'event' | 'model' | 'protocol' | 'ui' | 'character';
|
|
2
|
+
export type CanvasPartKind = 'agent' | 'connection' | 'rule' | 'skill' | 'test' | 'event' | 'model' | 'protocol' | 'ui' | 'character';
|
|
3
3
|
/**
|
|
4
4
|
* A part of an agent the Canvas may place (LOOP C-20): a connection to a
|
|
5
5
|
* server of the catalogue, a skill, a model, a rule, a test, an event, a
|
|
@@ -18,6 +18,12 @@ export type CanvasPartContribution = {
|
|
|
18
18
|
/** One line of what it is. */
|
|
19
19
|
says: string;
|
|
20
20
|
emoji?: string;
|
|
21
|
+
/**
|
|
22
|
+
* The harness it runs on, for an agent part (SHIELD SH-43): the catalogue
|
|
23
|
+
* harness's id, whose terms, credential and what it honours the Properties
|
|
24
|
+
* panel reads from `specs/harnesses`.
|
|
25
|
+
*/
|
|
26
|
+
harness?: string;
|
|
21
27
|
/**
|
|
22
28
|
* What it arrives with, spelled as the Appspec's text spells it: a
|
|
23
29
|
* connection's, a rule's, a test's or an event's object, a skill's, a
|
|
@@ -5,7 +5,7 @@ import { Box } from '@datalayer/primer-addons';
|
|
|
5
5
|
import { useIAMStore } from '@datalayer/core/lib/state/substates/IAMState';
|
|
6
6
|
import { agentServerOf } from '../../core';
|
|
7
7
|
import { BEHAVIOUR_WORDS, coverOf } from '../../apps/rules';
|
|
8
|
-
import { SESSION_RULES_WORDS, SESSION_RULE_CLASSES, SESSION_RULE_DECISIONS, addSessionRule, readSessionRules, removeSessionRule, sessionRuleInWords, sessionRuleOf, } from '../../apps/sessionRules';
|
|
8
|
+
import { FILE_AND_SHELL_RULES, SESSION_RULES_WORDS, SESSION_RULE_CLASSES, SESSION_RULE_DECISIONS, addSessionRule, readSessionRules, removeSessionRule, sessionRuleInWords, sessionRuleOf, } from '../../apps/sessionRules';
|
|
9
9
|
import { SAVE_WORDS, draftOfApproval } from '../../apps/saved';
|
|
10
10
|
import { useApproveToolRequest, useRejectToolRequest, useToolApprovalsQuery, } from '../../../hooks/useToolApprovals';
|
|
11
11
|
/** What the card says. */
|
|
@@ -105,7 +105,7 @@ export function SessionRules({ agentServerUrl, sessionUid, token, }) {
|
|
|
105
105
|
}
|
|
106
106
|
void act(() => addSessionRule(agentServerUrl, token, sessionUid, rule)).then(() => setName(''));
|
|
107
107
|
};
|
|
108
|
-
return (_jsxs(Box, { "data-testid": "session-rules", mt: 3, children: [_jsx(Heading, { as: "h4", sx: { fontSize: 1, mb: 1 }, children: SESSION_RULES_WORDS.title }), inForce?.unavailable ? (_jsx(Text, { as: "p", role: "alert", sx: { fontSize: 0, m: 0, mb: 1 }, children: SESSION_RULES_WORDS.unavailable(inForce.unavailable) })) : null, inForce && inForce.session_rules.length === 0 ? (_jsx(Text, { as: "p", sx: { fontSize: 1, color: 'fg.muted', m: 0 }, children: SESSION_RULES_WORDS.none })) : (_jsx(Box, { as: "ul", listStyle: "none", p: 0, m: 0, children: (inForce?.session_rules ?? []).map(rule => (_jsxs(Box, { as: "li", py: 1, fontSize: 1, display: "flex", gap: 2, alignItems: "center", children: [_jsx(Text, { sx: { fontWeight: 'semibold' }, children: rule.name }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: sessionRuleInWords(rule) }), _jsx(Button, { size: "small", variant: "invisible", disabled: busy, onClick: () => void act(() => removeSessionRule(agentServerUrl, token, sessionUid, rule.name)), children: SESSION_RULES_WORDS.remove })] }, rule.name))) })), _jsxs(Box, { display: "flex", gap: 1, mt: 1, flexWrap: "wrap", alignItems: "center", children: [_jsx(TextInput, { size: "small", value: name, placeholder: SESSION_RULES_WORDS.namePlaceholder, "aria-label": "The rule's name", onChange: event => setName(event.target.value) }), _jsx(Select, { size: "small", value: kind, "aria-label": "What it covers", onChange: event => setKind(event.target.value), children: SESSION_RULE_CLASSES.map(item => (_jsx(Select.Option, { value: item, children: SESSION_RULES_WORDS.classes[item] }, item))) }), _jsx(Select, { size: "small", value: decision, "aria-label": "What it says", onChange: event => setDecision(event.target.value), children: SESSION_RULE_DECISIONS.map(item => (_jsx(Select.Option, { value: item, children: SESSION_RULES_WORDS.decisions[item] }, item))) }), _jsx(Button, { size: "small", disabled: busy, onClick: add, children: SESSION_RULES_WORDS.add })] }), problem ? (_jsx(Text, { as: "p", role: "alert", sx: { fontSize: 0, m: 0, mt: 1 }, children: problem })) : null, _jsx(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted', mt: 1, mb: 0 }, children: SESSION_RULES_WORDS.goneWithSession })] }));
|
|
108
|
+
return (_jsxs(Box, { "data-testid": "session-rules", mt: 3, children: [_jsx(Heading, { as: "h4", sx: { fontSize: 1, mb: 1 }, children: SESSION_RULES_WORDS.title }), inForce?.unavailable ? (_jsx(Text, { as: "p", role: "alert", sx: { fontSize: 0, m: 0, mb: 1 }, children: SESSION_RULES_WORDS.unavailable(inForce.unavailable) })) : null, inForce && inForce.session_rules.length === 0 ? (_jsx(Text, { as: "p", sx: { fontSize: 1, color: 'fg.muted', m: 0 }, children: SESSION_RULES_WORDS.none })) : (_jsx(Box, { as: "ul", listStyle: "none", p: 0, m: 0, children: (inForce?.session_rules ?? []).map(rule => (_jsxs(Box, { as: "li", py: 1, fontSize: 1, display: "flex", gap: 2, alignItems: "center", children: [_jsx(Text, { sx: { fontWeight: 'semibold' }, children: rule.name }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: sessionRuleInWords(rule) }), _jsx(Button, { size: "small", variant: "invisible", disabled: busy, onClick: () => void act(() => removeSessionRule(agentServerUrl, token, sessionUid, rule.name)), children: SESSION_RULES_WORDS.remove })] }, rule.name))) })), _jsxs(Box, { display: "flex", gap: 1, mt: 1, flexWrap: "wrap", alignItems: "center", children: [_jsx(TextInput, { size: "small", value: name, placeholder: SESSION_RULES_WORDS.namePlaceholder, "aria-label": "The rule's name", onChange: event => setName(event.target.value) }), _jsx(Select, { size: "small", value: kind, "aria-label": "What it covers", onChange: event => setKind(event.target.value), children: SESSION_RULE_CLASSES.map(item => (_jsx(Select.Option, { value: item, children: SESSION_RULES_WORDS.classes[item] }, item))) }), _jsx(Select, { size: "small", value: decision, "aria-label": "What it says", onChange: event => setDecision(event.target.value), children: SESSION_RULE_DECISIONS.map(item => (_jsx(Select.Option, { value: item, children: SESSION_RULES_WORDS.decisions[item] }, item))) }), _jsx(Button, { size: "small", disabled: busy, onClick: add, children: SESSION_RULES_WORDS.add })] }), _jsx(Box, { display: "flex", gap: 1, mt: 1, flexWrap: "wrap", "data-testid": "session-rules-file-and-shell", children: FILE_AND_SHELL_RULES.map(rule => (_jsx(Button, { size: "small", variant: "invisible", disabled: busy, title: sessionRuleInWords(rule), onClick: () => void act(() => addSessionRule(agentServerUrl, token, sessionUid, rule)), children: rule.name }, rule.name))) }), problem ? (_jsx(Text, { as: "p", role: "alert", sx: { fontSize: 0, m: 0, mt: 1 }, children: problem })) : null, _jsx(Text, { as: "p", sx: { fontSize: 0, color: 'fg.muted', mt: 1, mb: 0 }, children: SESSION_RULES_WORDS.goneWithSession })] }));
|
|
109
109
|
}
|
|
110
110
|
export function AppRulesCard({ app, workspace, }) {
|
|
111
111
|
const token = useIAMStore(state => state.token) ?? '';
|
|
@@ -15,9 +15,17 @@ type PartsReader = {
|
|
|
15
15
|
}>;
|
|
16
16
|
};
|
|
17
17
|
/** The short ids an organization turns off, and the plugin each names. */
|
|
18
|
-
export declare const CANVAS_PART_SOURCES: readonly ["connections", "skills", "models", "rules", "tests", "events", "protocols", "ui", "characters"];
|
|
18
|
+
export declare const CANVAS_PART_SOURCES: readonly ["agents", "connections", "skills", "models", "rules", "tests", "events", "protocols", "ui", "characters"];
|
|
19
19
|
export type CanvasPartSource = (typeof CANVAS_PART_SOURCES)[number];
|
|
20
20
|
export declare const canvasPartsPluginName: (source: CanvasPartSource) => string;
|
|
21
|
+
/**
|
|
22
|
+
* The catalogue's harnesses as the agent (SHIELD SH-43): each harness an
|
|
23
|
+
* agent program over ACP behind the Shield, offered by the catalogue agent
|
|
24
|
+
* that runs on it — dropped on the surface, it is the application's agent.
|
|
25
|
+
* A harness no agent of the catalogue runs yet is offered off, and says so;
|
|
26
|
+
* `custom` is an organization's own and never offered here.
|
|
27
|
+
*/
|
|
28
|
+
export declare const AGENT_PARTS: CanvasPartContribution[];
|
|
21
29
|
/** A connection to each server of the catalogue: read-only, as its owner, every tool. */
|
|
22
30
|
export declare const CONNECTION_PARTS: CanvasPartContribution[];
|
|
23
31
|
/** Each skill of the catalogue. */
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { buildReactorFromPlugins, contribution, definePlugin, } from '@datalayer/reactor';
|
|
15
15
|
import { MCP_SERVER_LIBRARY } from '../../../specs/mcpServers';
|
|
16
|
+
import { listAgentspecs } from '../../../specs/agents';
|
|
17
|
+
import { listHarnesses } from '../../../specs/harnesses';
|
|
16
18
|
import { listChatModels } from '../../../specs/models';
|
|
17
19
|
import { getSkillSpecs } from '../../../specs/skills';
|
|
18
20
|
import { listProtocolSpecs } from '../../../specs/protocols';
|
|
@@ -21,6 +23,7 @@ import { ASSISTANT_CHARACTERS } from '../../../chat/assistant/characters';
|
|
|
21
23
|
import { LoopCanvasPart, } from '../../core/canvasParts';
|
|
22
24
|
/** The short ids an organization turns off, and the plugin each names. */
|
|
23
25
|
export const CANVAS_PART_SOURCES = [
|
|
26
|
+
'agents',
|
|
24
27
|
'connections',
|
|
25
28
|
'skills',
|
|
26
29
|
'models',
|
|
@@ -39,6 +42,30 @@ const partsPlugin = (source, displayName, parts) => definePlugin({
|
|
|
39
42
|
octicon: 'package',
|
|
40
43
|
contributes: parts.map(part => contribution(LoopCanvasPart, part, { id: part.id })),
|
|
41
44
|
});
|
|
45
|
+
/**
|
|
46
|
+
* The catalogue's harnesses as the agent (SHIELD SH-43): each harness an
|
|
47
|
+
* agent program over ACP behind the Shield, offered by the catalogue agent
|
|
48
|
+
* that runs on it — dropped on the surface, it is the application's agent.
|
|
49
|
+
* A harness no agent of the catalogue runs yet is offered off, and says so;
|
|
50
|
+
* `custom` is an organization's own and never offered here.
|
|
51
|
+
*/
|
|
52
|
+
export const AGENT_PARTS = listHarnesses()
|
|
53
|
+
.filter(harness => harness.id !== 'custom')
|
|
54
|
+
.map(harness => {
|
|
55
|
+
const agent = listAgentspecs().find(spec => spec.harness === harness.id);
|
|
56
|
+
return {
|
|
57
|
+
id: `agent:${harness.id}`,
|
|
58
|
+
kind: 'agent',
|
|
59
|
+
label: harness.name,
|
|
60
|
+
says: agent
|
|
61
|
+
? `${agent.name}: ${harness.description}`
|
|
62
|
+
: `${harness.description} No agent of the catalogue runs on it yet.`,
|
|
63
|
+
emoji: '🛡️',
|
|
64
|
+
harness: harness.id,
|
|
65
|
+
example: agent?.id ?? '',
|
|
66
|
+
enabled: Boolean(agent) && harness.enabled,
|
|
67
|
+
};
|
|
68
|
+
});
|
|
42
69
|
/** A connection to each server of the catalogue: read-only, as its owner, every tool. */
|
|
43
70
|
export const CONNECTION_PARTS = Object.values(MCP_SERVER_LIBRARY).map(server => ({
|
|
44
71
|
id: `connection:${server.id}`,
|
|
@@ -210,6 +237,7 @@ export const CHARACTER_PARTS = ASSISTANT_CHARACTERS.map(character => ({
|
|
|
210
237
|
enabled: true,
|
|
211
238
|
}));
|
|
212
239
|
export const CANVAS_PART_PLUGINS = [
|
|
240
|
+
partsPlugin('agents', 'Agents', AGENT_PARTS),
|
|
213
241
|
partsPlugin('connections', 'Connections', CONNECTION_PARTS),
|
|
214
242
|
partsPlugin('skills', 'Skills', SKILL_PARTS),
|
|
215
243
|
partsPlugin('models', 'Models', MODEL_PARTS),
|