glove-foundry 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-UNHGCCSA.js → chunk-P65RT7H5.js} +486 -49
- package/dist/cli.js +536 -352
- package/dist/index.js +1 -1
- package/docs/building-with-foundry.md +39 -2
- package/docs/inspector.md +35 -2
- package/package.json +4 -3
- package/templates/minimal/README.md +58 -0
- package/templates/minimal/agents/assistant/agent.ts +28 -0
- package/templates/minimal/agents/assistant/tools/current-time.tool.ts +17 -0
- package/templates/minimal/env.example +2 -0
- package/templates/minimal/foundry.application.ts +12 -0
- package/templates/minimal/foundry.config.ts +6 -0
- package/templates/minimal/gitignore +3 -0
- package/templates/travel-concierge/README.md +203 -0
- package/templates/travel-concierge/agents/concierge/actions/reply.action.ts +6 -0
- package/templates/travel-concierge/agents/concierge/agent.ts +106 -0
- package/templates/travel-concierge/agents/concierge/apps/calendar.app.ts +67 -0
- package/templates/travel-concierge/agents/concierge/composition.ts +18 -0
- package/templates/travel-concierge/agents/concierge/events/message-received.event.ts +7 -0
- package/templates/travel-concierge/agents/concierge/events/message-sent.event.ts +7 -0
- package/templates/travel-concierge/agents/concierge/layers/trip-context.layer.ts +23 -0
- package/templates/travel-concierge/agents/concierge/memory/traveller.memory.ts +19 -0
- package/templates/travel-concierge/agents/concierge/predicates/mentions-trip.predicate.ts +17 -0
- package/templates/travel-concierge/agents/concierge/schedules/trip-countdown.ts +16 -0
- package/templates/travel-concierge/agents/concierge/subscribers/usage.subscriber.ts +16 -0
- package/templates/travel-concierge/agents/concierge/tools/find-flights.tool.ts +49 -0
- package/templates/travel-concierge/agents/concierge/topology.ts +35 -0
- package/templates/travel-concierge/agents/concierge/transmissions/messaging.transmission.ts +57 -0
- package/templates/travel-concierge/agents/concierge/workbench.ts +26 -0
- package/templates/travel-concierge/env.example +3 -0
- package/templates/travel-concierge/foundry.application.ts +18 -0
- package/templates/travel-concierge/foundry.config.ts +9 -0
- package/templates/travel-concierge/gitignore +3 -0
- package/templates/travel-concierge/lib/demo-model.ts +72 -0
- package/templates/travel-concierge/src/client.ts +22 -0
|
@@ -3159,7 +3159,14 @@ var PHOSPHOR_ICON_PATHS = {
|
|
|
3159
3159
|
search: "M229.66,218.34l-50.07-50.06a88.11,88.11,0,1,0-11.31,11.31l50.06,50.07a8,8,0,0,0,11.32-11.32ZM40,112a72,72,0,1,1,72,72A72.08,72.08,0,0,1,40,112Z",
|
|
3160
3160
|
definitions: "M229.66,189.66l-32,32a8,8,0,0,1-11.32,0l-32-32a8,8,0,0,1,11.32-11.32L184,196.69V139.31l-56-56-56,56v57.38l18.34-18.35a8,8,0,0,1,11.32,11.32l-32,32a8,8,0,0,1-11.32,0l-32-32a8,8,0,0,1,11.32-11.32L56,196.69V136a8,8,0,0,1,2.34-5.66L120,68.69V24a8,8,0,0,1,16,0V68.69l61.66,61.65A8,8,0,0,1,200,136v60.69l18.34-18.35a8,8,0,0,1,11.32,11.32Z",
|
|
3161
3161
|
secure: "M208,40H48A16,16,0,0,0,32,56v56c0,52.72,25.52,84.67,46.93,102.19,23.06,18.86,46,25.26,47,25.53a8,8,0,0,0,4.2,0c1-.27,23.91-6.67,47-25.53C198.48,196.67,224,164.72,224,112V56A16,16,0,0,0,208,40Zm0,72c0,37.07-13.66,67.16-40.6,89.42A129.3,129.3,0,0,1,128,223.62a128.25,128.25,0,0,1-38.92-21.81C61.82,179.51,48,149.3,48,112l0-56,160,0ZM82.34,141.66a8,8,0,0,1,11.32-11.32L112,148.69l50.34-50.35a8,8,0,0,1,11.32,11.32l-56,56a8,8,0,0,1-11.32,0Z",
|
|
3162
|
-
external: "M200,64V168a8,8,0,0,1-16,0V83.31L69.66,197.66a8,8,0,0,1-11.32-11.32L172.69,72H88a8,8,0,0,1,0-16H192A8,8,0,0,1,200,64Z"
|
|
3162
|
+
external: "M200,64V168a8,8,0,0,1-16,0V83.31L69.66,197.66a8,8,0,0,1-11.32-11.32L172.69,72H88a8,8,0,0,1,0-16H192A8,8,0,0,1,200,64Z",
|
|
3163
|
+
copy: "M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z",
|
|
3164
|
+
check: "M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z",
|
|
3165
|
+
clock: "M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm64-88a8,8,0,0,1-8,8H128a8,8,0,0,1-8-8V72a8,8,0,0,1,16,0v48h48A8,8,0,0,1,192,128Z",
|
|
3166
|
+
refresh: "M240,56v48a8,8,0,0,1-8,8H184a8,8,0,0,1,0-16H211.4L184.81,71.64l-.25-.24a80,80,0,1,0-1.67,114.78,8,8,0,0,1,11,11.63A95.44,95.44,0,0,1,128,224h-1.32A96,96,0,1,1,195.75,60L224,85.8V56a8,8,0,1,1,16,0Z",
|
|
3167
|
+
warning: "M236.8,188.09,149.35,36.22h0a24.76,24.76,0,0,0-42.7,0L19.2,188.09a23.51,23.51,0,0,0,0,23.72A24.35,24.35,0,0,0,40.55,224h174.9a24.35,24.35,0,0,0,21.33-12.19A23.51,23.51,0,0,0,236.8,188.09ZM222.93,203.8a8.5,8.5,0,0,1-7.48,4.2H40.55a8.5,8.5,0,0,1-7.48-4.2,7.59,7.59,0,0,1,0-7.72L120.52,44.21a8.75,8.75,0,0,1,15,0l87.45,151.87A7.59,7.59,0,0,1,222.93,203.8ZM120,144V104a8,8,0,0,1,16,0v40a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,180Z",
|
|
3168
|
+
caretDown: "M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,53.66,90.34L128,164.69l74.34-74.35a8,8,0,0,1,11.32,11.32Z",
|
|
3169
|
+
funnel: "M200,136a8,8,0,0,1-8,8H64a8,8,0,0,1,0-16H192A8,8,0,0,1,200,136Zm32-56H24a8,8,0,0,0,0,16H232a8,8,0,0,0,0-16Zm-80,96H104a8,8,0,0,0,0,16h48a8,8,0,0,0,0-16Z"
|
|
3163
3170
|
};
|
|
3164
3171
|
function renderPhosphorIcon(name, className = "icon") {
|
|
3165
3172
|
return `<svg class="${className}" data-phosphor="${name}" viewBox="0 0 256 256" fill="currentColor" aria-hidden="true"><path d="${PHOSPHOR_ICON_PATHS[name]}"/></svg>`;
|
|
@@ -3174,7 +3181,7 @@ function renderGloveMark() {
|
|
|
3174
3181
|
var ICON_PATHS_JSON = JSON.stringify(PHOSPHOR_ICON_PATHS);
|
|
3175
3182
|
var DASHBOARD_SCRIPT = String.raw`
|
|
3176
3183
|
const ICON_PATHS=${ICON_PATHS_JSON};
|
|
3177
|
-
const state={manifest:null,instances:[],subscriptions:[],connections:[],runs:[],events:[],health:null,transmissions:[],accounts:[],routes:[],bindings:[],activations:[],conversations:{},workspaces:{},
|
|
3184
|
+
const state={manifest:null,instances:[],subscriptions:[],connections:[],runs:[],events:[],health:null,transmissions:[],accounts:[],routes:[],bindings:[],activations:[],conversations:{},workspaces:{},showAllEvents:false,eventCategory:"all",ready:false,cursor:-1,liveAt:null};
|
|
3178
3185
|
const $=id=>document.getElementById(id);
|
|
3179
3186
|
const esc=value=>String(value??"").replace(/[&<>"']/g,c=>({"&":"&","<":"<",">":">","\"":""","'":"'"}[c]));
|
|
3180
3187
|
const attr=value=>esc(value).replace(/\x60/g,"`");
|
|
@@ -3185,6 +3192,98 @@ const jsonText=value=>JSON.stringify(value,null,2);
|
|
|
3185
3192
|
const count=(n,word)=>n+" "+word+(n===1?"":"s");
|
|
3186
3193
|
const icon=(name,className)=>'<svg class="'+(className||"icon")+'" data-phosphor="'+name+'" viewBox="0 0 256 256" fill="currentColor" aria-hidden="true"><path d="'+ICON_PATHS[name]+'"/></svg>';
|
|
3187
3194
|
|
|
3195
|
+
/* ---- Elapsed time. Operators read runs in "how long ago" and "how long did it take". ---- */
|
|
3196
|
+
const DAY=86400000;
|
|
3197
|
+
function spanText(ms){
|
|
3198
|
+
const seconds=Math.round(ms/1000);if(seconds<60)return seconds+"s";
|
|
3199
|
+
const minutes=Math.round(seconds/60);if(minutes<60)return minutes+"m";
|
|
3200
|
+
const hours=Math.round(minutes/60);if(hours<24)return hours+"h";
|
|
3201
|
+
const days=Math.round(hours/24);if(days<30)return days+"d";
|
|
3202
|
+
return Math.round(days/30)+"mo";
|
|
3203
|
+
}
|
|
3204
|
+
function relTime(iso){
|
|
3205
|
+
if(!iso)return"—";
|
|
3206
|
+
const then=new Date(iso).getTime();if(!Number.isFinite(then))return"—";
|
|
3207
|
+
const delta=Date.now()-then;
|
|
3208
|
+
if(delta<-1000)return"in "+spanText(-delta);
|
|
3209
|
+
if(delta<5000)return"just now";
|
|
3210
|
+
return spanText(delta)+" ago";
|
|
3211
|
+
}
|
|
3212
|
+
function durationText(ms){
|
|
3213
|
+
if(!Number.isFinite(ms)||ms<0)return"—";
|
|
3214
|
+
if(ms<1000)return Math.round(ms)+"ms";
|
|
3215
|
+
if(ms<60000)return(ms/1000).toFixed(ms<10000?2:1)+"s";
|
|
3216
|
+
const minutes=Math.floor(ms/60000);const seconds=Math.round(ms%60000/1000);
|
|
3217
|
+
if(minutes<60)return minutes+"m "+seconds+"s";
|
|
3218
|
+
return Math.floor(minutes/60)+"h "+minutes%60+"m";
|
|
3219
|
+
}
|
|
3220
|
+
function fullDate(iso){return iso?new Date(iso).toLocaleString([],{weekday:"short",month:"short",day:"numeric",year:"numeric",hour:"2-digit",minute:"2-digit",second:"2-digit"}):"Not recorded"}
|
|
3221
|
+
function isLiveRun(run){return run.status==="running"||run.status==="pending"}
|
|
3222
|
+
function runStartedAt(run){return run.startedAt||run.createdAt}
|
|
3223
|
+
function runDurationMs(run){
|
|
3224
|
+
const start=new Date(runStartedAt(run)).getTime();if(!Number.isFinite(start))return NaN;
|
|
3225
|
+
const finished=run.completedAt?new Date(run.completedAt).getTime():isLiveRun(run)?Date.now():NaN;
|
|
3226
|
+
return Number.isFinite(finished)?finished-start:NaN;
|
|
3227
|
+
}
|
|
3228
|
+
/* A relative timestamp that the one-second ticker keeps current without a re-render. */
|
|
3229
|
+
function relCell(iso){return iso?'<time class="rel" data-rel="'+attr(iso)+'" title="'+attr(fullDate(iso))+'">'+esc(relTime(iso))+'</time>':'<span class="muted">—</span>'}
|
|
3230
|
+
function durCell(run){
|
|
3231
|
+
const live=isLiveRun(run)&&Boolean(runStartedAt(run));
|
|
3232
|
+
const text=durationText(runDurationMs(run));
|
|
3233
|
+
return'<span class="dur'+(live?" live":"")+'"'+(live?' data-since="'+attr(runStartedAt(run))+'"':"")+' title="'+attr(live?"Still running":"Total run duration")+'">'+esc(text)+'</span>';
|
|
3234
|
+
}
|
|
3235
|
+
function tickTimes(){
|
|
3236
|
+
document.querySelectorAll("[data-rel]").forEach(node=>{const next=relTime(node.dataset.rel);if(node.textContent!==next)node.textContent=next});
|
|
3237
|
+
document.querySelectorAll("[data-since]").forEach(node=>{const started=new Date(node.dataset.since).getTime();const next=Number.isFinite(started)?durationText(Date.now()-started):"—";if(node.textContent!==next)node.textContent=next});
|
|
3238
|
+
}
|
|
3239
|
+
|
|
3240
|
+
/* ---- Copy. Ids are truncated for scanning, so every one of them stays retrievable. ---- */
|
|
3241
|
+
function copyButton(value,label){return'<button class="copy-btn" type="button" data-copy="'+attr(value)+'" title="Copy '+attr(label||value)+'" aria-label="Copy '+attr(label||value)+'">'+icon("copy")+icon("check","icon copy-ok")+'</button>'}
|
|
3242
|
+
function idCell(value,extraClass){return'<span class="id-cell '+(extraClass||"")+'"><span class="mono">'+esc(short(value))+'</span>'+copyButton(value)+'</span>'}
|
|
3243
|
+
async function copyValue(button){
|
|
3244
|
+
const value=button.dataset.copy;
|
|
3245
|
+
try{
|
|
3246
|
+
if(navigator.clipboard&&window.isSecureContext){await navigator.clipboard.writeText(value)}
|
|
3247
|
+
else{
|
|
3248
|
+
const area=document.createElement("textarea");area.value=value;area.setAttribute("readonly","");
|
|
3249
|
+
area.style.position="fixed";area.style.top="-1000px";area.style.opacity="0";
|
|
3250
|
+
document.body.appendChild(area);area.select();document.execCommand("copy");area.remove();
|
|
3251
|
+
}
|
|
3252
|
+
button.classList.add("copied");setTimeout(()=>button.classList.remove("copied"),1200);
|
|
3253
|
+
toast("Copied "+short(value));
|
|
3254
|
+
}catch{toast("Could not copy to the clipboard")}
|
|
3255
|
+
}
|
|
3256
|
+
|
|
3257
|
+
/* ---- View state. Live updates repaint the page, so the parts a person is
|
|
3258
|
+
actively using -- scroll, focus, caret, expanded rows -- survive it. ---- */
|
|
3259
|
+
function captureView(){
|
|
3260
|
+
const active=document.activeElement;
|
|
3261
|
+
return{
|
|
3262
|
+
scroll:window.scrollY,
|
|
3263
|
+
focus:active&&active.id?active.id:null,
|
|
3264
|
+
caret:active&&typeof active.selectionStart==="number"?active.selectionStart:null,
|
|
3265
|
+
open:[...document.querySelectorAll("[data-event-key].open")].map(node=>node.dataset.eventKey),
|
|
3266
|
+
details:[...document.querySelectorAll("details[data-persist]")].filter(node=>node.open).map(node=>node.dataset.persist)
|
|
3267
|
+
};
|
|
3268
|
+
}
|
|
3269
|
+
function restoreView(view){
|
|
3270
|
+
if(!view)return;
|
|
3271
|
+
if(view.open.length)document.querySelectorAll("[data-event-key]").forEach(node=>{if(view.open.includes(node.dataset.eventKey))node.classList.add("open")});
|
|
3272
|
+
if(view.details.length)document.querySelectorAll("details[data-persist]").forEach(node=>{if(view.details.includes(node.dataset.persist))node.open=true});
|
|
3273
|
+
if(view.focus){
|
|
3274
|
+
const node=$(view.focus);
|
|
3275
|
+
if(node&&node!==document.activeElement){
|
|
3276
|
+
node.focus({preventScroll:true});
|
|
3277
|
+
if(view.caret!==null&&typeof node.setSelectionRange==="function"){try{node.setSelectionRange(view.caret,view.caret)}catch{}}
|
|
3278
|
+
}
|
|
3279
|
+
}
|
|
3280
|
+
if(view.scroll)window.scrollTo(0,view.scroll);
|
|
3281
|
+
}
|
|
3282
|
+
function skeleton(rows){
|
|
3283
|
+
let html="";for(let index=0;index<(rows||5);index++)html+='<div class="skeleton-row"><span style="width:'+(46+index*7%34)+'%"></span><span style="width:'+(16+index*5%14)+'%"></span></div>';
|
|
3284
|
+
return'<div class="skeleton" aria-busy="true" aria-label="Loading">'+html+'</div>';
|
|
3285
|
+
}
|
|
3286
|
+
|
|
3188
3287
|
async function api(url,options){
|
|
3189
3288
|
const response=await fetch(url,options);let body;
|
|
3190
3289
|
try{body=await response.json()}catch{body={error:"The runtime returned an unreadable response."}}
|
|
@@ -3202,6 +3301,31 @@ function metric(label,value,note,accent){return'<section class="card metric '+(a
|
|
|
3202
3301
|
function dataLinks(){
|
|
3203
3302
|
document.querySelectorAll("[data-link]").forEach(node=>node.onclick=event=>{if(event.metaKey||event.ctrlKey||event.shiftKey||event.altKey)return;event.preventDefault();go(node.getAttribute("href"))});
|
|
3204
3303
|
document.querySelectorAll("[data-go]").forEach(node=>node.onclick=event=>{if(event.target.closest("a,button"))return;go(node.dataset.go)});
|
|
3304
|
+
document.querySelectorAll("[data-copy]").forEach(node=>node.onclick=event=>{event.preventDefault();event.stopPropagation();void copyValue(node)});
|
|
3305
|
+
syncCursor();
|
|
3306
|
+
}
|
|
3307
|
+
/* ---- Row cursor. j/k walk a list and Enter opens it, without reaching for the mouse. ---- */
|
|
3308
|
+
function cursorRows(){return[...document.querySelectorAll("[data-go]")]}
|
|
3309
|
+
function syncCursor(){
|
|
3310
|
+
const rows=cursorRows();
|
|
3311
|
+
if(state.cursor>=rows.length)state.cursor=rows.length-1;
|
|
3312
|
+
rows.forEach((row,index)=>row.classList.toggle("cursor",index===state.cursor));
|
|
3313
|
+
}
|
|
3314
|
+
function moveCursor(delta){
|
|
3315
|
+
const rows=cursorRows();if(!rows.length)return;
|
|
3316
|
+
state.cursor=state.cursor<0?(delta>0?0:rows.length-1):Math.min(rows.length-1,Math.max(0,state.cursor+delta));
|
|
3317
|
+
syncCursor();
|
|
3318
|
+
const row=rows[state.cursor];
|
|
3319
|
+
if(row)row.scrollIntoView({block:"nearest"});
|
|
3320
|
+
}
|
|
3321
|
+
function openCursor(){
|
|
3322
|
+
const row=cursorRows()[state.cursor];
|
|
3323
|
+
if(row)go(row.dataset.go);
|
|
3324
|
+
}
|
|
3325
|
+
function isTyping(){
|
|
3326
|
+
const node=document.activeElement;if(!node)return false;
|
|
3327
|
+
const tag=node.tagName;
|
|
3328
|
+
return tag==="INPUT"||tag==="TEXTAREA"||tag==="SELECT"||node.isContentEditable;
|
|
3205
3329
|
}
|
|
3206
3330
|
function workspaceIds(){const ids=new Set();state.instances.forEach(x=>ids.add(x.workspaceId));state.subscriptions.forEach(x=>ids.add(x.workspaceId));state.connections.forEach(x=>ids.add(x.workspaceId));state.runs.forEach(x=>{if(x.workspaceId)ids.add(x.workspaceId)});return[...ids].filter(Boolean).sort()}
|
|
3207
3331
|
function definition(id){return state.manifest?.agents?.agents?.find(x=>x.id===id)}
|
|
@@ -3233,8 +3357,8 @@ function renderOverview(){
|
|
|
3233
3357
|
let html=pageHead("Runtime at a glance","What is happening now","Start here. Foundry separates code-defined agents from their runtime instances, then records every invocation as a run.",'<a class="button" href="/agents" data-link>Browse agents</a><button class="button primary" data-new-run>Start a run</button>');
|
|
3234
3358
|
html+='<div class="grid cols-4">'+metric("Agent definitions",definitions.length,"Discovered from the agents folder",true)+metric("Runtime instances",state.instances.length,"Persisted agent identities")+metric("Active work",live.length,live.length?"Runs currently pending or running":"No work in flight")+metric("Attention needed",failureRuns().length,"Failed runs in retained history")+'</div>';
|
|
3235
3359
|
html+='<div class="grid cols-2" style="margin-top:16px">';
|
|
3236
|
-
const activity=recentRuns.map(run=>'<tr class="clickable" data-go="/runs/'+attr(run.id)+'"><td><span class="primary-cell">'+esc(run.agent)+'</span><span class="secondary">'+esc(
|
|
3237
|
-
html+=card("Recent runs",'<div class="table-wrap"><table class="table"><thead><tr><th>Agent</th><th>Status</th><th>
|
|
3360
|
+
const activity=recentRuns.map(run=>'<tr class="clickable'+(run.status==="failed"?" row-failed":"")+(isLiveRun(run)?" row-live":"")+'" data-go="/runs/'+attr(run.id)+'"><td><span class="primary-cell">'+esc(run.agent)+'</span><span class="secondary">'+esc(explainRun(run))+'</span></td><td>'+status(run.status)+'</td><td>'+idCell(run.id)+'</td><td>'+durCell(run)+'</td><td>'+relCell(run.createdAt)+'</td></tr>').join("");
|
|
3361
|
+
html+=card("Recent runs",'<div class="table-wrap"><table class="table"><thead><tr><th>Agent</th><th>Status</th><th>Run</th><th>Duration</th><th>Started</th></tr></thead><tbody>'+activity+'</tbody></table></div>'+(activity?"":(state.ready?empty("No runs yet","Start a run to see its progress and trace here."):skeleton(4)))+'<div class="card-foot"><a class="link inline-icon" href="/runs" data-link>View all runs '+icon("external")+'</a></div>',count(state.runs.length,"run"),"span-2");
|
|
3238
3362
|
const map=definitions.slice(0,6).map(def=>{const instances=state.instances.filter(x=>x.definitionId===def.id);return'<tr class="clickable" data-go="/agents/'+attr(def.id)+'"><td><span class="primary-cell">'+esc(def.id)+'</span><span class="secondary">'+esc(def.description)+'</span></td><td>'+instances.length+'</td><td>'+esc((def.workingEnvironment?"Working environment · ":"")+(def.repl?def.repl+" REPL":"No REPL"))+'</td></tr>'}).join("");
|
|
3239
3363
|
html+=card("Agent map",'<div class="table-wrap"><table class="table"><thead><tr><th>Definition</th><th>Instances</th><th>Mounted surface</th></tr></thead><tbody>'+map+'</tbody></table></div>',count(definitions.length,"definition"));
|
|
3240
3364
|
const automationCount=state.activations.length+state.subscriptions.length+state.connections.length;
|
|
@@ -3247,7 +3371,7 @@ function renderAgents(){
|
|
|
3247
3371
|
let html=pageHead("Code and runtime","Agents","A definition is the code-discovered recipe. An instance is persisted runtime data with its own context, apps, playbooks, and conversations.",'<button class="button primary" data-new-run>Start a run</button>');
|
|
3248
3372
|
html+='<div class="callout"><span class="symbol">'+icon("definitions")+'</span><div><b>Definitions and instances are intentionally separate.</b><p>Editing a file changes what can be assembled. Updating an instance changes what one runtime identity actually has installed.</p></div></div>';
|
|
3249
3373
|
html+='<div class="definition-grid">'+definitions.map(def=>{const instances=state.instances.filter(x=>x.definitionId===def.id);const features=[def.workingEnvironment?"VFS":null,def.repl?def.repl+" repl":null,def.mesh?"mesh":null].filter(Boolean);return'<a class="definition-card" href="/agents/'+attr(def.id)+'" data-link><span class="definition-icon">'+icon("agent")+'</span><span class="definition-open">'+icon("external")+'</span><h3>'+esc(def.id)+'</h3><p>'+esc(def.description)+'</p><div>'+tags(features)+'</div><div class="definition-meta"><b>'+count(instances.length,"instance")+'</b><span class="muted mono" style="font-size:9px">'+esc(def.file)+'</span></div></a>'}).join("")+'</div>';
|
|
3250
|
-
const rows=recent(state.instances).map(instance=>'<tr class="clickable" data-go="/instances/'+attr(instance.id)+'"><td
|
|
3374
|
+
const rows=recent(state.instances).map(instance=>'<tr class="clickable" data-go="/instances/'+attr(instance.id)+'"><td>'+idCell(instance.id)+'<span class="secondary">'+esc(instance.workspaceId)+'</span></td><td><a class="link" href="/agents/'+attr(instance.definitionId)+'" data-link>'+esc(instance.definitionId)+'</a></td><td>'+instance.installations.length+'</td><td>'+instance.playbooks.length+'</td><td>'+relCell(instance.updatedAt)+'</td></tr>').join("");
|
|
3251
3375
|
html+=card("Runtime instances",'<div class="table-wrap"><table class="table"><thead><tr><th>Instance</th><th>Definition</th><th>Installed</th><th>Playbooks</th><th>Updated</th></tr></thead><tbody>'+rows+'</tbody></table></div>'+(rows?"":empty("No runtime instances","Start a run or create an instance from a definition.")),count(state.instances.length,"instance"));
|
|
3252
3376
|
$("content").innerHTML=html;document.querySelectorAll("[data-new-run]").forEach(x=>x.onclick=openRunDrawer);dataLinks();
|
|
3253
3377
|
}
|
|
@@ -3258,7 +3382,7 @@ function renderDefinition(id){
|
|
|
3258
3382
|
const instances=state.instances.filter(x=>x.definitionId===id);const caps=state.manifest?.definitions?.[id]?.capabilities||{tools:[],applications:[],mcp:[],memory:[]};const surfaces=state.manifest?.definitions?.[id]?.surfaces||{layers:[],subscribers:[]};
|
|
3259
3383
|
let html=pageHead("Agent definition",id,def.description,'<button class="button" data-create-instance="'+attr(id)+'">Create instance</button><button class="button primary" data-new-run="'+attr(id)+'">Start a run</button>');
|
|
3260
3384
|
html+='<div class="detail-strip"><div><label>File route</label><strong class="mono">'+esc(def.file)+'</strong></div><div><label>Assembly</label><strong>'+esc(def.assembly)+" · "+esc(def.handler)+' handler</strong></div><div><label>Runtime surfaces</label><strong>'+(def.workingEnvironment?"Working environment":"No working environment")+(def.repl?" · "+esc(def.repl)+" REPL":"")+'</strong></div><div><label>Lazy fields</label><strong>'+esc(def.lazy.length?def.lazy.join(", "):"None")+'</strong></div></div>';
|
|
3261
|
-
const rows=instances.map(x=>'<tr class="clickable" data-go="/instances/'+attr(x.id)+'"><td
|
|
3385
|
+
const rows=instances.map(x=>'<tr class="clickable" data-go="/instances/'+attr(x.id)+'"><td>'+idCell(x.id)+'<span class="secondary">'+esc(x.workspaceId)+'</span></td><td>'+x.installations.length+'</td><td>'+x.playbooks.length+'</td><td>'+relCell(x.updatedAt)+'</td></tr>').join("");
|
|
3262
3386
|
html+=card("Runtime instances",'<div class="table-wrap"><table class="table"><thead><tr><th>Instance</th><th>Installed</th><th>Playbooks</th><th>Updated</th></tr></thead><tbody>'+rows+'</tbody></table></div>'+(rows?"":empty("No instance exists","Create one now or let a playbook provision one when an inbound event matches.")),count(instances.length,"instance"));
|
|
3263
3387
|
html+='<div class="grid cols-2">';
|
|
3264
3388
|
const groups=[["Shared tools",caps.tools],["Applications",caps.applications],["MCP",caps.mcp],["Memory",caps.memory]];
|
|
@@ -3272,25 +3396,101 @@ function renderInstance(id){
|
|
|
3272
3396
|
const instance=state.instances.find(x=>x.id===id);if(!instance)return renderNotFound("Agent instance");
|
|
3273
3397
|
crumbs([{label:"Agents",href:"/agents"},{label:instance.definitionId,href:"/agents/"+instance.definitionId},{label:short(id)}]);
|
|
3274
3398
|
const conversations=state.conversations[id]||[];const runs=recent(state.runs.filter(x=>x.agentId===id));
|
|
3275
|
-
let html=pageHead("Runtime instance",short(id),"Persisted runtime data assembled from "+instance.definitionId+". This identity can be updated without changing the code definition.",'<button class="button primary" data-instance-run>Send message</button>');
|
|
3276
|
-
html+='<div class="detail-strip"><div><label>Definition</label><strong><a class="link" href="/agents/'+attr(instance.definitionId)+'" data-link>'+esc(instance.definitionId)+'</a></strong></div><div><label>Workspace</label><strong><a class="link mono" href="/workspaces/'+attr(instance.workspaceId)+'" data-link>'+esc(instance.workspaceId)+'</a></strong></div><div><label>Conversations</label><strong>'+conversations.length+'</strong></div><div><label>Last updated</label><strong>'+
|
|
3399
|
+
let html=pageHead("Runtime instance",short(id),"Persisted runtime data assembled from "+instance.definitionId+". This identity can be updated without changing the code definition.",'<button class="button" data-copy="'+attr(id)+'">'+icon("copy")+'<span>Copy instance id</span></button><button class="button primary" data-instance-run>Send message</button>');
|
|
3400
|
+
html+='<div class="detail-strip"><div><label>Definition</label><strong><a class="link" href="/agents/'+attr(instance.definitionId)+'" data-link>'+esc(instance.definitionId)+'</a></strong></div><div><label>Workspace</label><strong><a class="link mono" href="/workspaces/'+attr(instance.workspaceId)+'" data-link>'+esc(instance.workspaceId)+'</a></strong></div><div><label>Conversations</label><strong>'+conversations.length+'</strong></div><div><label>Last updated</label><strong>'+relCell(instance.updatedAt)+'</strong></div></div>';
|
|
3277
3401
|
html+='<div class="grid cols-2">';
|
|
3278
|
-
const convRows=recent(conversations).map(c=>'<tr><td><span class="primary-cell">'+esc(c.title||"Untitled conversation")+'</span
|
|
3402
|
+
const convRows=recent(conversations).map(c=>'<tr><td><span class="primary-cell">'+esc(c.title||"Untitled conversation")+'</span>'+idCell(c.id)+'</td><td>'+relCell(c.updatedAt)+'</td></tr>').join("");
|
|
3279
3403
|
html+=card("Conversations",'<div class="table-wrap"><table class="table"><thead><tr><th>Conversation</th><th>Updated</th></tr></thead><tbody>'+convRows+'</tbody></table></div>'+(convRows?"":empty("No conversations","The first message creates a conversation for this instance.")),count(conversations.length,"conversation"));
|
|
3280
|
-
const runRows=runs.slice(0,8).map(run=>'<tr class="clickable" data-go="/runs/'+attr(run.id)+'"><td
|
|
3281
|
-
html+=card("Recent runs",'<div class="table-wrap"><table class="table"><thead><tr><th>Run</th><th>Status</th><th>Started</th></tr></thead><tbody>'+runRows+'</tbody></table></div>'+(runRows?"":empty("No runs","Send this instance a message to begin.")),count(runs.length,"run"));
|
|
3404
|
+
const runRows=runs.slice(0,8).map(run=>'<tr class="clickable'+(run.status==="failed"?" row-failed":"")+(isLiveRun(run)?" row-live":"")+'" data-go="/runs/'+attr(run.id)+'"><td>'+idCell(run.id)+'<span class="secondary">'+esc(explainRun(run))+'</span></td><td>'+status(run.status)+'</td><td>'+durCell(run)+'</td><td>'+relCell(run.createdAt)+'</td></tr>').join("");
|
|
3405
|
+
html+=card("Recent runs",'<div class="table-wrap"><table class="table"><thead><tr><th>Run</th><th>Status</th><th>Duration</th><th>Started</th></tr></thead><tbody>'+runRows+'</tbody></table></div>'+(runRows?"":empty("No runs","Send this instance a message to begin."))+(runs.length>8?'<div class="card-foot"><a class="link inline-icon" href="/runs?q='+encodeURIComponent(instance.id)+'" data-link>View all '+count(runs.length,"run")+' '+icon("external")+'</a></div>':""),count(runs.length,"run"));
|
|
3282
3406
|
html+=card("Installed capabilities",'<div class="card-body">'+(instance.installations.length?instance.installations.map(x=>'<div class="cap">'+esc(x.kind)+" · "+esc(x.id)+(x.accountId?" · account "+esc(x.accountId):"")+'</div>').join(""):empty("Nothing installed","Applications, MCP servers, and shared tools are instance data."))+'</div>',count(instance.installations.length,"installation"));
|
|
3283
3407
|
html+=card("Playbooks and context",'<div class="card-body"><div class="summary-box"><dl><dt>Playbooks</dt><dd>'+esc(instance.playbooks.map(x=>x.name||x.id).join(", ")||"None")+'</dd><dt>Context keys</dt><dd>'+esc(Object.keys(instance.context||{}).join(", ")||"None")+'</dd><dt>Provisioning key</dt><dd class="mono">'+esc(instance.provisioningKey||"Directly provisioned")+'</dd></dl></div><details><summary class="link">View stored instance data</summary><pre class="json">'+esc(jsonText(instance))+'</pre></details></div>',"Persisted data");
|
|
3284
3408
|
html+='</div>';$("content").innerHTML=html;document.querySelector("[data-instance-run]").onclick=()=>openRunDrawer(instance.definitionId,id);dataLinks();
|
|
3285
3409
|
}
|
|
3286
3410
|
|
|
3411
|
+
/* ---- Run filters live in the URL, so a filtered view is a link you can send. ---- */
|
|
3412
|
+
function runQuery(){
|
|
3413
|
+
const params=new URLSearchParams(location.search);
|
|
3414
|
+
return{status:params.get("status")||"all",source:params.get("source")||"all",q:params.get("q")||""};
|
|
3415
|
+
}
|
|
3416
|
+
function setRunQuery(patch){
|
|
3417
|
+
const next=Object.assign(runQuery(),patch);
|
|
3418
|
+
const params=new URLSearchParams();
|
|
3419
|
+
if(next.status!=="all")params.set("status",next.status);
|
|
3420
|
+
if(next.source!=="all")params.set("source",next.source);
|
|
3421
|
+
if(next.q)params.set("q",next.q);
|
|
3422
|
+
const search=params.toString();
|
|
3423
|
+
history.replaceState({},"",location.pathname+(search?"?"+search:""));
|
|
3424
|
+
return next;
|
|
3425
|
+
}
|
|
3426
|
+
function runSource(run){return run.input?.source?.kind||"direct"}
|
|
3427
|
+
function matchesStatus(run,status){
|
|
3428
|
+
if(status==="all")return true;
|
|
3429
|
+
if(status==="running")return isLiveRun(run);
|
|
3430
|
+
return run.status===status;
|
|
3431
|
+
}
|
|
3432
|
+
function filterRuns(runs,query){
|
|
3433
|
+
const term=query.q.trim().toLowerCase();
|
|
3434
|
+
return runs.filter(run=>{
|
|
3435
|
+
if(!matchesStatus(run,query.status))return false;
|
|
3436
|
+
if(query.source!=="all"&&runSource(run)!==query.source)return false;
|
|
3437
|
+
if(!term)return true;
|
|
3438
|
+
return(run.agent+" "+run.id+" "+(run.agentId||"")+" "+jsonText(run.input)).toLowerCase().includes(term);
|
|
3439
|
+
});
|
|
3440
|
+
}
|
|
3441
|
+
function statusSegments(runs,active){
|
|
3442
|
+
const options=[["all","All"],["running","Running"],["completed","Completed"],["failed","Failed"],["cancelled","Cancelled"]];
|
|
3443
|
+
return'<div class="segmented" role="tablist" aria-label="Filter runs by status">'+options.map(option=>{
|
|
3444
|
+
const total=runs.filter(run=>matchesStatus(run,option[0])).length;
|
|
3445
|
+
const on=active===option[0];
|
|
3446
|
+
return'<button type="button" role="tab" aria-selected="'+(on?"true":"false")+'" class="'+(on?"active":"")+'" data-status="'+attr(option[0])+'">'+esc(option[1])+'<span class="seg-count">'+total+'</span></button>';
|
|
3447
|
+
}).join("")+'</div>';
|
|
3448
|
+
}
|
|
3449
|
+
function runRow(run,showAgent){
|
|
3450
|
+
const failed=run.status==="failed";
|
|
3451
|
+
return'<tr class="clickable run-row'+(failed?" row-failed":"")+(isLiveRun(run)?" row-live":"")+'" data-go="/runs/'+attr(run.id)+'">'
|
|
3452
|
+
+(showAgent===false?"":'<td><span class="primary-cell">'+esc(run.agent)+'</span><span class="secondary">'+esc(explainRun(run))+'</span></td>')
|
|
3453
|
+
+'<td>'+status(run.status)+'</td>'
|
|
3454
|
+
+'<td>'+idCell(run.id)+'</td>'
|
|
3455
|
+
+'<td><span class="tag">'+esc(runSource(run))+'</span></td>'
|
|
3456
|
+
+'<td>'+durCell(run)+'</td>'
|
|
3457
|
+
+'<td>'+(run.attempts>1?'<span class="attempts warn">'+run.attempts+" / "+run.maxAttempts+'</span>':'<span class="attempts">'+run.attempts+" / "+run.maxAttempts+'</span>')+'</td>'
|
|
3458
|
+
+'<td>'+relCell(run.createdAt)+'</td></tr>';
|
|
3459
|
+
}
|
|
3287
3460
|
function renderRuns(){
|
|
3288
|
-
crumbs([{label:"Runs"}]);
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
const
|
|
3292
|
-
html
|
|
3293
|
-
|
|
3461
|
+
crumbs([{label:"Runs"}]);
|
|
3462
|
+
const query=runQuery();const all=recent(state.runs);const runs=filterRuns(all,query);
|
|
3463
|
+
const sources=[...new Set(all.map(runSource))].sort();
|
|
3464
|
+
const live=all.filter(isLiveRun).length;
|
|
3465
|
+
let html=pageHead("Execution history","Runs","Each invocation has one status, one result, and a chronological trace. Filters stay in the address bar, so any view here is a link you can share.",'<button class="button" data-refresh>'+icon("refresh")+'<span>Refresh</span></button><button class="button primary" data-new-run>'+icon("plus")+'<span>Start a run</span></button>');
|
|
3466
|
+
html+='<div class="toolbar">'+statusSegments(all,query.status)
|
|
3467
|
+
+'<div class="toolbar-right">'
|
|
3468
|
+
+'<label class="search-field"><span class="search-field-icon">'+icon("search")+'</span><input id="run-search" value="'+attr(query.q)+'" placeholder="Search agent, run id, or input…" aria-label="Search runs" /></label>'
|
|
3469
|
+
+'<label class="select-field"><span class="select-field-icon">'+icon("funnel")+'</span><select id="run-source" aria-label="Filter runs by source"><option value="all">Any source</option>'+sources.map(kind=>'<option value="'+attr(kind)+'"'+(query.source===kind?" selected":"")+'>'+esc(kind)+'</option>').join("")+'</select></label>'
|
|
3470
|
+
+'</div></div>';
|
|
3471
|
+
const activeFilters=[query.status!=="all"?"status "+query.status:null,query.source!=="all"?"source "+query.source:null,query.q?'matching "'+query.q+'"':null].filter(Boolean);
|
|
3472
|
+
if(activeFilters.length)html+='<div class="filter-note"><span>Showing '+count(runs.length,"run")+' of '+all.length+' · '+esc(activeFilters.join(" · "))+'</span><button class="link" type="button" data-clear-filters>Clear filters</button></div>';
|
|
3473
|
+
const rows=runs.map(run=>runRow(run)).join("");
|
|
3474
|
+
const meta=count(runs.length,"run")+(live?" · "+live+" in flight":"");
|
|
3475
|
+
html+=card("All runs",'<div class="table-wrap"><table class="table runs-table"><thead><tr><th>Agent</th><th>Status</th><th>Run</th><th>Source</th><th>Duration</th><th>Attempts</th><th>Started</th></tr></thead><tbody>'+rows+'</tbody></table></div>'+(rows?"":(state.ready?empty(all.length?"No runs match these filters":"No runs yet",all.length?"Clear a filter or widen the search to see the rest.":"Start a run to watch it assemble, work, and complete here."):skeleton(5))),meta);
|
|
3476
|
+
html+='<p class="hint">'+esc("j / k to move · Enter to open · / to search · c to start a run · r to refresh")+'</p>';
|
|
3477
|
+
$("content").innerHTML=html;
|
|
3478
|
+
state.cursor=-1;
|
|
3479
|
+
document.querySelectorAll("[data-status]").forEach(node=>node.onclick=()=>{setRunQuery({status:node.dataset.status});renderRuns()});
|
|
3480
|
+
$("run-source").onchange=event=>{setRunQuery({source:event.target.value});renderRuns()};
|
|
3481
|
+
const search=$("run-search");
|
|
3482
|
+
search.oninput=event=>{
|
|
3483
|
+
const caret=event.target.selectionStart;
|
|
3484
|
+
setRunQuery({q:event.target.value});
|
|
3485
|
+
renderRuns();
|
|
3486
|
+
const next=$("run-search");
|
|
3487
|
+
if(next){next.focus();try{next.setSelectionRange(caret,caret)}catch{}}
|
|
3488
|
+
};
|
|
3489
|
+
const clear=document.querySelector("[data-clear-filters]");
|
|
3490
|
+
if(clear)clear.onclick=()=>{history.replaceState({},"",location.pathname);renderRuns()};
|
|
3491
|
+
document.querySelector("[data-new-run]").onclick=()=>openRunDrawer();
|
|
3492
|
+
document.querySelector("[data-refresh]").onclick=()=>void refreshActivity();
|
|
3493
|
+
dataLinks();
|
|
3294
3494
|
}
|
|
3295
3495
|
|
|
3296
3496
|
function phaseData(run,events){
|
|
@@ -3302,18 +3502,58 @@ function phaseData(run,events){
|
|
|
3302
3502
|
{name:run.status==="failed"?"Failed":run.status==="cancelled"?"Cancelled":run.status==="completed"?"Completed":"In progress",detail:explainRun(run),state:run.status==="failed"||run.status==="cancelled"?"error":run.status==="completed"?"complete":"active",time:run.completedAt||time(/run\.(completed|failed|cancelled)/)}
|
|
3303
3503
|
];
|
|
3304
3504
|
}
|
|
3505
|
+
function eventOffset(run,event){
|
|
3506
|
+
const base=new Date(run.createdAt).getTime();const at=new Date(event.timestamp).getTime();
|
|
3507
|
+
if(!Number.isFinite(base)||!Number.isFinite(at))return"";
|
|
3508
|
+
return"+"+durationText(Math.max(0,at-base));
|
|
3509
|
+
}
|
|
3510
|
+
function runErrorText(run){
|
|
3511
|
+
if(run.status!=="failed")return"";
|
|
3512
|
+
if(typeof run.error==="string")return run.error;
|
|
3513
|
+
if(run.error&&typeof run.error==="object")return run.error.message||jsonText(run.error);
|
|
3514
|
+
return"The runtime recorded a failure without a message.";
|
|
3515
|
+
}
|
|
3305
3516
|
function renderRun(id){
|
|
3306
|
-
const run=state.runs.find(x=>x.id===id);if(!run)return renderNotFound("Run");
|
|
3307
|
-
const
|
|
3517
|
+
const run=state.runs.find(x=>x.id===id);if(!run)return renderNotFound("Run");
|
|
3518
|
+
const events=eventsFor(id);
|
|
3519
|
+
const categories=[...new Set(events.map(event=>event.category))].filter(Boolean).sort();
|
|
3520
|
+
let visibleEvents=state.showAllEvents?events:events.filter(isKeyRunEvent);
|
|
3521
|
+
if(state.eventCategory!=="all")visibleEvents=visibleEvents.filter(event=>event.category===state.eventCategory);
|
|
3308
3522
|
crumbs([{label:"Runs",href:"/runs"},{label:short(id)}]);
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
html
|
|
3313
|
-
const
|
|
3314
|
-
|
|
3315
|
-
html+=
|
|
3316
|
-
|
|
3523
|
+
const actions=(isLiveRun(run)?'<button class="button danger" data-cancel>Cancel run</button>':"")
|
|
3524
|
+
+'<button class="button" data-copy="'+attr(run.id)+'">'+icon("copy")+'<span>Copy run id</span></button>'
|
|
3525
|
+
+'<button class="button primary" data-rerun>'+icon("refresh")+'<span>Run again</span></button>';
|
|
3526
|
+
let html=pageHead("Run detail",run.agent+" · "+short(id),"Follow the high-level phases first. Expand individual events only when you need raw adapter or model evidence.",actions);
|
|
3527
|
+
const failure=runErrorText(run);
|
|
3528
|
+
if(failure)html+='<div class="alert danger"><span class="symbol">'+icon("warning")+'</span><div><b>This run failed'+(run.attempts>1?" after "+count(run.attempts,"attempt"):"")+'.</b><p>'+esc(failure)+'</p></div></div>';
|
|
3529
|
+
if(isLiveRun(run))html+='<div class="alert live"><span class="symbol"><i class="pulse-dot"></i></span><div><b>This run is still '+esc(run.status)+'.</b><p>New events stream in below as the runtime records them.</p></div></div>';
|
|
3530
|
+
html+='<div class="detail-strip">'
|
|
3531
|
+
+'<div><label>Status</label><strong>'+status(run.status)+'</strong></div>'
|
|
3532
|
+
+'<div><label>Duration</label><strong>'+durCell(run)+'</strong></div>'
|
|
3533
|
+
+'<div><label>Source</label><strong>'+esc(runSource(run))+'</strong></div>'
|
|
3534
|
+
+'<div><label>Agent instance</label><strong>'+(run.agentId?'<a class="link mono" href="/instances/'+attr(run.agentId)+'" data-link>'+esc(short(run.agentId))+'</a>':"Not recorded")+'</strong></div>'
|
|
3535
|
+
+'<div><label>Attempts</label><strong>'+run.attempts+" of "+run.maxAttempts+'</strong></div>'
|
|
3536
|
+
+'</div>';
|
|
3537
|
+
html+='<div class="grid cols-3"><section class="card span-2"><div class="card-head"><h2>Run spine</h2><span class="meta">'+count(events.length,"event")+'</span></div><div class="card-body"><div class="trace-note">This shows observable work intent and outcomes. It does not expose private hidden chain-of-thought.</div><div class="run-spine">'+phaseData(run,events).map(p=>'<div class="phase '+p.state+'"><span class="phase-dot"></span><div><strong>'+esc(p.name)+'</strong><p>'+esc(p.detail)+'</p></div><time title="'+attr(fullDate(p.time))+'">'+esc(fmtTime(p.time))+'</time></div>').join("")+'</div></div></section>';
|
|
3538
|
+
html+=card("Result",'<div class="card-body"><div class="summary-box"><dl><dt>Created</dt><dd>'+relCell(run.createdAt)+'</dd><dt>Started</dt><dd>'+relCell(run.startedAt)+'</dd><dt>Finished</dt><dd>'+relCell(run.completedAt)+'</dd><dt>Timeout</dt><dd>'+esc(durationText(run.timeoutMs))+'</dd></dl></div><details data-persist="output" open><summary class="link">Output</summary><pre class="json">'+esc(jsonText(run.output??run.error??null))+'</pre></details><details data-persist="input"><summary class="link">Input</summary><pre class="json">'+esc(jsonText(run.input))+'</pre></details></div>',"Recorded outcome");
|
|
3539
|
+
html+='</div>';
|
|
3540
|
+
const eventRows=visibleEvents.map((event,index)=>{
|
|
3541
|
+
const key=event.id||event.type+":"+event.timestamp+":"+index;
|
|
3542
|
+
return'<div class="event-row" data-event-key="'+attr(key)+'">'
|
|
3543
|
+
+'<button class="event-toggle" type="button"><span class="event-time">'+esc(fmtTime(event.timestamp))+'</span><span class="event-offset mono">'+esc(eventOffset(run,event))+'</span><span class="event-category">'+esc(event.category)+'</span><span class="event-type">'+esc(event.type)+'</span><span class="event-caret">'+icon("caretDown")+'</span></button>'
|
|
3544
|
+
+'<div class="event-detail"><div class="event-detail-head"><span class="muted mono">'+esc(event.type)+'</span>'+copyButton(jsonText(event.data),"event payload")+'</div><pre class="json">'+esc(jsonText(event.data))+'</pre></div></div>';
|
|
3545
|
+
}).join("");
|
|
3546
|
+
const categoryChips=categories.length>1?'<div class="chips">'+['all'].concat(categories).map(name=>'<button type="button" class="chip'+(state.eventCategory===name?" active":"")+'" data-category="'+attr(name)+'">'+esc(name==="all"?"All categories":name)+'</button>').join("")+'</div>':"";
|
|
3547
|
+
const eventControl='<div class="event-controls">'+categoryChips+'<div class="event-controls-right"><span class="muted">'+esc(state.showAllEvents?"Showing every retained event, including assembly and process detail.":"Showing the lifecycle events that explain this run.")+'</span><button class="button" type="button" data-toggle-events>'+esc(state.showAllEvents?"Show key events":"Show all "+events.length+" events")+'</button></div></div>';
|
|
3548
|
+
html+=card("Observable event trace",eventControl+'<div class="event-list">'+(eventRows||empty("No events to show","Clear the category filter or show every retained event."))+'</div>',count(visibleEvents.length,"event"));
|
|
3549
|
+
$("content").innerHTML=html;
|
|
3550
|
+
document.querySelectorAll(".event-toggle").forEach(node=>node.onclick=()=>node.parentElement.classList.toggle("open"));
|
|
3551
|
+
document.querySelectorAll("[data-category]").forEach(node=>node.onclick=()=>{state.eventCategory=node.dataset.category;renderRun(id)});
|
|
3552
|
+
document.querySelector("[data-toggle-events]").onclick=()=>{state.showAllEvents=!state.showAllEvents;renderRun(id)};
|
|
3553
|
+
const cancel=document.querySelector("[data-cancel]");if(cancel)cancel.onclick=()=>void cancelRun(id);
|
|
3554
|
+
const rerun=document.querySelector("[data-rerun]");
|
|
3555
|
+
if(rerun)rerun.onclick=()=>openRunDrawer(state.instances.find(x=>x.id===run.agentId)?.definitionId,run.agentId,typeof run.input?.message?.text==="string"?run.input.message.text:typeof run.input?.message==="string"?run.input.message:"");
|
|
3556
|
+
dataLinks();
|
|
3317
3557
|
}
|
|
3318
3558
|
|
|
3319
3559
|
function renderAutomations(){
|
|
@@ -3323,7 +3563,7 @@ function renderAutomations(){
|
|
|
3323
3563
|
html+=card("Schedules and sleeps",'<div class="table-wrap"><table class="table"><thead><tr><th>Activation</th><th>Agent</th><th>Status</th><th>Timing</th><th>Origin</th></tr></thead><tbody>'+actRows+'</tbody></table></div>'+(actRows?"":empty("No future activations","Agents create schedules and sleeps through Foundry utility tools.")),count(state.activations.length,"activation"));
|
|
3324
3564
|
const subRows=state.subscriptions.map(x=>'<tr><td><span class="primary-cell">'+esc(x.playbook?.name||x.playbook?.id||x.id)+'</span><span class="secondary">'+esc(short(x.id))+'</span></td><td>'+status(x.enabled?"enabled":"disabled")+'</td><td>'+esc(x.targets.map(t=>t.definitionId+" · "+t.provisioning.mode).join(", "))+'</td><td>'+esc(x.workspaceId)+'</td></tr>').join("");
|
|
3325
3565
|
html+=card("Playbook listeners",'<div class="table-wrap"><table class="table"><thead><tr><th>Playbook</th><th>State</th><th>Targets / provisioning</th><th>Workspace</th></tr></thead><tbody>'+subRows+'</tbody></table></div>'+(subRows?"":empty("No playbook subscriptions","Runtime-defined subscriptions appear when a frontend or adapter installs them.")),count(state.subscriptions.length,"subscription"));
|
|
3326
|
-
const connRows=state.connections.map(x=>'<tr><td><span class="primary-cell">'+esc(x.applicationId)+'</span><span class="secondary">'+esc(x.connectionId)+'</span></td><td>'+status(x.status)+'</td><td>'+esc(x.definitionId)+'</td><td>'+x.routeIds.length+'</td><td>'+
|
|
3566
|
+
const connRows=state.connections.map(x=>'<tr><td><span class="primary-cell">'+esc(x.applicationId)+'</span><span class="secondary">'+esc(x.connectionId)+'</span></td><td>'+status(x.status)+'</td><td>'+esc(x.definitionId)+'</td><td>'+x.routeIds.length+'</td><td>'+relCell(x.lastEventAt)+'</td></tr>').join("");
|
|
3327
3567
|
html+=card("Inbound application workers",'<div class="table-wrap"><table class="table"><thead><tr><th>Application / connection</th><th>Status</th><th>Definition</th><th>Routes</th><th>Last event</th></tr></thead><tbody>'+connRows+'</tbody></table></div>'+(connRows?"":empty("No inbound workers","A connection starts only when an installed app has an active inbound playbook.")),count(state.connections.length,"connection"));
|
|
3328
3568
|
$("content").innerHTML=html;dataLinks();
|
|
3329
3569
|
}
|
|
@@ -3348,16 +3588,32 @@ function renderWorkspaces(id){
|
|
|
3348
3588
|
const data=state.workspaces[id]||{entries:[],inbox:[],tasks:[],environment:[]};const localInstances=state.instances.filter(x=>x.workspaceId===id);
|
|
3349
3589
|
let html=pageHead("Shared data","Workspace · "+id,"Inspect collaboration state shared by this workspace. Secret environment values are intentionally never exposed.",'<select class="button" id="workspace-select">'+ids.map(x=>'<option value="'+attr(x)+'" '+(x===id?"selected":"")+'>'+esc(x)+'</option>').join("")+'</select>');
|
|
3350
3590
|
html+='<div class="grid cols-4">'+metric("Agent instances",localInstances.length,"Runtime identities in this workspace",true)+metric("Shared entries",data.entries.length,"Documents and structured values")+metric("Inbox items",data.inbox.length,"Cross-agent and external handoffs")+metric("Open tasks",data.tasks.filter(x=>x.status==="open"||x.status==="in-progress").length,"Work still requiring action")+'</div>';
|
|
3351
|
-
|
|
3352
|
-
|
|
3591
|
+
const counts={entries:data.entries.length,inbox:data.inbox.length,tasks:data.tasks.length,environment:data.environment.length};
|
|
3592
|
+
html+='<div class="workspace-tabs" role="tablist">'+["entries","inbox","tasks","environment"].map(tab=>'<button type="button" role="tab" data-tab="'+attr(tab)+'">'+esc(tab[0].toUpperCase()+tab.slice(1))+'<span class="seg-count">'+counts[tab]+'</span></button>').join("")+'</div><div id="workspace-panel"></div>';
|
|
3593
|
+
$("content").innerHTML=html;
|
|
3594
|
+
$("workspace-select").onchange=e=>go("/workspaces/"+encodeURIComponent(e.target.value));
|
|
3595
|
+
document.querySelectorAll("[data-tab]").forEach(x=>x.onclick=()=>{
|
|
3596
|
+
const params=new URLSearchParams(location.search);
|
|
3597
|
+
if(x.dataset.tab==="entries")params.delete("tab");else params.set("tab",x.dataset.tab);
|
|
3598
|
+
const search=params.toString();
|
|
3599
|
+
history.replaceState({},"",location.pathname+(search?"?"+search:""));
|
|
3600
|
+
renderWorkspacePanel(data);
|
|
3601
|
+
});
|
|
3602
|
+
renderWorkspacePanel(data);dataLinks();
|
|
3603
|
+
}
|
|
3604
|
+
function workspaceTab(){
|
|
3605
|
+
const tab=new URLSearchParams(location.search).get("tab")||"entries";
|
|
3606
|
+
return["entries","inbox","tasks","environment"].includes(tab)?tab:"entries";
|
|
3353
3607
|
}
|
|
3354
3608
|
function renderWorkspacePanel(data){
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
if(
|
|
3359
|
-
if(
|
|
3360
|
-
|
|
3609
|
+
const active=workspaceTab();
|
|
3610
|
+
document.querySelectorAll("[data-tab]").forEach(x=>{const on=x.dataset.tab===active;x.classList.toggle("active",on);x.setAttribute("aria-selected",on?"true":"false")});
|
|
3611
|
+
const values=data[active]||[];let body="";
|
|
3612
|
+
if(active==="entries")body=values.length?'<div class="kv">'+values.map(x=>'<div class="kv-item"><strong>'+esc(x.key)+'</strong><small>Updated '+relTime(x.updatedAt)+'</small><div class="kv-value">'+esc(typeof x.value==="string"?x.value:jsonText(x.value))+'</div></div>').join("")+'</div>':empty("No shared entries","Agents can place documents and structured data into this workspace.");
|
|
3613
|
+
if(active==="inbox")body=values.length?'<div class="table-wrap"><table class="table"><thead><tr><th>Topic</th><th>Agent</th><th>Status</th><th>Updated</th></tr></thead><tbody>'+recent(values).map(x=>'<tr><td><span class="primary-cell">'+esc(x.topic)+'</span><span class="secondary">'+esc(short(x.id))+'</span></td><td>'+esc(short(x.agentId||"shared"))+'</td><td>'+status(x.status)+'</td><td>'+relCell(x.updatedAt)+'</td></tr>').join("")+'</tbody></table></div>':empty("Inbox is clear","Shared handoffs and external requests will appear here.");
|
|
3614
|
+
if(active==="tasks")body=values.length?'<div class="table-wrap"><table class="table"><thead><tr><th>Task</th><th>Owner</th><th>Status</th><th>Updated</th></tr></thead><tbody>'+recent(values).map(x=>'<tr><td><span class="primary-cell">'+esc(x.title)+'</span><span class="secondary">'+esc(x.detail||short(x.id))+'</span></td><td>'+esc(short(x.agentId||"workspace"))+'</td><td>'+status(x.status)+'</td><td>'+relCell(x.updatedAt)+'</td></tr>').join("")+'</tbody></table></div>':empty("No tasks","Agents can create shared tasks for work that spans conversations.");
|
|
3615
|
+
if(active==="environment")body='<div class="callout"><span class="symbol">'+icon("secure")+'</span><div><b>Only safe values are visible.</b><p>Credential material remains inside user-owned adapters and is never returned by this endpoint.</p></div></div>'+(values.length?'<div class="kv">'+values.map(x=>'<div class="kv-item"><strong>'+esc(x.key)+'</strong><small>'+esc(x.scope)+'</small><div class="kv-value">'+esc(typeof x.value==="string"?x.value:jsonText(x.value))+'</div></div>').join("")+'</div>':empty("No public environment values","Mount an environment adapter to expose non-secret context."));
|
|
3616
|
+
$("workspace-panel").innerHTML=card(active[0].toUpperCase()+active.slice(1),'<div class="card-body">'+body+'</div>',count(values.length,"item"));
|
|
3361
3617
|
}
|
|
3362
3618
|
|
|
3363
3619
|
function renderNotFound(label){crumbs([{label:"Not found"}]);$("content").innerHTML=pageHead("Inspector",label+" not found","The requested runtime record does not exist or is no longer retained.",'<a class="button" href="/" data-link>Return to overview</a>');dataLinks()}
|
|
@@ -3365,28 +3621,71 @@ async function loadBase(){
|
|
|
3365
3621
|
const endpoints=["/api/manifest","/api/agent-instances","/api/playbook-subscriptions","/api/application-connections","/api/runs","/api/events","/health","/api/transmissions","/api/accounts","/api/routes","/api/bindings","/api/activations"];
|
|
3366
3622
|
const results=await Promise.all(endpoints.map(url=>api(url)));[state.manifest,state.instances,state.subscriptions,state.connections,state.runs,state.events,state.health,state.transmissions,state.accounts,state.routes,state.bindings,state.activations]=results;renderHealth();
|
|
3367
3623
|
}
|
|
3624
|
+
/* Every observability event used to trigger a full reload and repaint, which
|
|
3625
|
+
made a busy run unreadable. Events are coalesced into one refresh instead,
|
|
3626
|
+
and the repaint restores whatever the operator was doing. */
|
|
3627
|
+
let refreshTimer=null;let refreshing=false;let refreshQueued=false;
|
|
3628
|
+
function scheduleRefresh(){
|
|
3629
|
+
markLive();
|
|
3630
|
+
if(refreshTimer)return;
|
|
3631
|
+
refreshTimer=setTimeout(()=>{refreshTimer=null;void refreshActivity()},350);
|
|
3632
|
+
}
|
|
3633
|
+
function markLive(){
|
|
3634
|
+
state.liveAt=Date.now();
|
|
3635
|
+
const chip=document.querySelector(".live-chip");
|
|
3636
|
+
if(!chip)return;
|
|
3637
|
+
chip.classList.remove("pulse");void chip.offsetWidth;chip.classList.add("pulse");
|
|
3638
|
+
}
|
|
3368
3639
|
async function refreshActivity(){
|
|
3369
|
-
|
|
3640
|
+
if(refreshing){refreshQueued=true;return}
|
|
3641
|
+
refreshing=true;
|
|
3642
|
+
try{
|
|
3643
|
+
const endpoints=["/api/agent-instances","/api/playbook-subscriptions","/api/application-connections","/api/runs","/api/events","/api/activations","/health"];
|
|
3644
|
+
const values=await Promise.all(endpoints.map(url=>api(url)));
|
|
3645
|
+
[state.instances,state.subscriptions,state.connections,state.runs,state.events,state.activations,state.health]=values;
|
|
3646
|
+
renderHealth();
|
|
3647
|
+
$("event-state").textContent="Live updates";
|
|
3648
|
+
await navigate(false,true);
|
|
3649
|
+
}catch{$("event-state").textContent="Refresh paused"}
|
|
3650
|
+
finally{
|
|
3651
|
+
refreshing=false;
|
|
3652
|
+
if(refreshQueued){refreshQueued=false;scheduleRefresh()}
|
|
3653
|
+
}
|
|
3370
3654
|
}
|
|
3371
3655
|
function renderHealth(){const ok=Boolean(state.health?.ok);$("runtime-state").classList.toggle("bad",!ok);$("runtime-state").querySelector("span").textContent=ok?"Runtime healthy":"Runtime needs attention"}
|
|
3372
|
-
async function navigate(load=true){
|
|
3656
|
+
async function navigate(load=true,preserve=false){
|
|
3373
3657
|
const current=route();navState(current.section);
|
|
3658
|
+
const view=preserve?captureView():null;
|
|
3374
3659
|
try{
|
|
3375
3660
|
if(load&¤t.section==="instances"&¤t.id)await ensureConversations(current.id);
|
|
3376
3661
|
if(load&¤t.section==="workspaces"&¤t.id)await ensureWorkspace(current.id);
|
|
3377
3662
|
if(current.section==="overview")renderOverview();else if(current.section==="agents"&&!current.id)renderAgents();else if(current.section==="agents")renderDefinition(current.id);else if(current.section==="instances")renderInstance(current.id);else if(current.section==="runs"&&!current.id)renderRuns();else if(current.section==="runs")renderRun(current.id);else if(current.section==="automations")renderAutomations();else if(current.section==="integrations")renderIntegrations();else if(current.section==="workspaces")renderWorkspaces(current.id);else renderNotFound("Page");
|
|
3378
3663
|
}catch(error){$("content").innerHTML='<div class="form-error">'+esc(error.message)+'</div>';console.error(error)}
|
|
3664
|
+
restoreView(view);
|
|
3665
|
+
tickTimes();
|
|
3379
3666
|
}
|
|
3380
3667
|
|
|
3381
|
-
function openRunDrawer(definitionId,instanceId){
|
|
3668
|
+
function openRunDrawer(definitionId,instanceId,message){
|
|
3382
3669
|
const definitions=state.manifest?.agents?.agents||[];$("run-definition").innerHTML=definitions.map(x=>'<option value="'+attr(x.id)+'">'+esc(x.id)+'</option>').join("");
|
|
3383
|
-
if(typeof definitionId==="string"&&definition(definitionId))$("run-definition").value=definitionId;
|
|
3670
|
+
if(typeof definitionId==="string"&&definition(definitionId))$("run-definition").value=definitionId;
|
|
3671
|
+
updateInstanceOptions(instanceId);
|
|
3672
|
+
$("run-message").value=typeof message==="string"?message:"";
|
|
3673
|
+
$("run-form-error").classList.add("hidden");
|
|
3674
|
+
setRunSubmitting(false);
|
|
3675
|
+
$("drawer").classList.add("open");
|
|
3676
|
+
setTimeout(()=>{const box=$("run-message");box.focus();box.setSelectionRange(box.value.length,box.value.length)},20);
|
|
3677
|
+
}
|
|
3678
|
+
function setRunSubmitting(pending){
|
|
3679
|
+
const submit=$("run-submit");
|
|
3680
|
+
submit.disabled=pending;
|
|
3681
|
+
submit.classList.toggle("pending",pending);
|
|
3682
|
+
submit.textContent=pending?"Starting…":"Start run";
|
|
3384
3683
|
}
|
|
3385
3684
|
function closeRunDrawer(){$("drawer").classList.remove("open")}
|
|
3386
3685
|
function updateInstanceOptions(selected){const definitionId=$("run-definition").value;const instances=state.instances.filter(x=>x.definitionId===definitionId);$("run-instance").innerHTML='<option value="">Create a new instance</option>'+instances.map(x=>'<option value="'+attr(x.id)+'">'+esc(short(x.id))+" · "+esc(x.workspaceId)+'</option>').join("");if(selected&&instances.some(x=>x.id===selected))$("run-instance").value=selected}
|
|
3387
3686
|
async function createInstance(definitionId){try{const workspace=workspaceIds()[0]||"foundry-dashboard";const instance=await api("/api/agent-instances",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({definitionId,workspaceId:workspace})});state.instances=[...state.instances,instance];toast("Instance created");go("/instances/"+encodeURIComponent(instance.id))}catch(error){toast(error.message)}}
|
|
3388
3687
|
async function startRun(event){
|
|
3389
|
-
event.preventDefault();const errorNode=$("run-form-error");errorNode.classList.add("hidden");
|
|
3688
|
+
event.preventDefault();const errorNode=$("run-form-error");errorNode.classList.add("hidden");setRunSubmitting(true);
|
|
3390
3689
|
try{
|
|
3391
3690
|
const definitionId=$("run-definition").value;let instance=state.instances.find(x=>x.id===$("run-instance").value);
|
|
3392
3691
|
if(!instance){instance=await api("/api/agent-instances",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({definitionId,workspaceId:workspaceIds()[0]||"foundry-dashboard"})});state.instances=[...state.instances,instance]}
|
|
@@ -3394,6 +3693,7 @@ async function startRun(event){
|
|
|
3394
3693
|
if(!conversation){conversation=await api("/api/conversations",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({agentId:instance.id,title:"Foundry inspector"})});state.conversations[instance.id]=[conversation]}
|
|
3395
3694
|
const run=await api("/api/conversations/"+encodeURIComponent(conversation.id)+"/messages",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({agentId:instance.id,message:$("run-message").value})});state.runs=[...state.runs,run];closeRunDrawer();toast("Run accepted");go("/runs/"+encodeURIComponent(run.id));
|
|
3396
3695
|
}catch(error){errorNode.textContent=error.message;errorNode.classList.remove("hidden")}
|
|
3696
|
+
finally{setRunSubmitting(false)}
|
|
3397
3697
|
}
|
|
3398
3698
|
async function cancelRun(id){try{await api("/api/runs/"+encodeURIComponent(id)+"/cancel",{method:"POST"});toast("Cancellation requested");await refreshActivity()}catch(error){toast(error.message)}}
|
|
3399
3699
|
|
|
@@ -3404,8 +3704,30 @@ function searchItems(){
|
|
|
3404
3704
|
function renderSearch(){const term=$("search-input").value.trim().toLowerCase();const matches=searchItems().filter(x=>!term||(x.label+" "+x.detail).toLowerCase().includes(term)).slice(0,12);$("search-results").innerHTML=matches.map(x=>'<a class="search-result" href="'+attr(x.path)+'" data-search-link><strong>'+esc(x.label)+'</strong><small>'+esc(x.detail)+'</small></a>').join("")||empty("No matches","Try an agent id, instance id, run id, or page name.");document.querySelectorAll("[data-search-link]").forEach(x=>x.onclick=e=>{e.preventDefault();closeSearch();go(x.getAttribute("href"))})}
|
|
3405
3705
|
function openSearch(){$("search-modal").classList.add("open");$("search-input").value="";renderSearch();setTimeout(()=>$("search-input").focus(),20)}function closeSearch(){$("search-modal").classList.remove("open")}
|
|
3406
3706
|
|
|
3407
|
-
$("new-run").onclick=()=>openRunDrawer();$("close-drawer").onclick=closeRunDrawer;$("cancel-run").onclick=closeRunDrawer;$("drawer").onclick=e=>{if(e.target===$("drawer"))closeRunDrawer()};$("run-definition").onchange=()=>updateInstanceOptions();$("run-form").onsubmit=startRun;$("open-search").onclick=openSearch;$("search-input").oninput=renderSearch;$("search-modal").onclick=e=>{if(e.target===$("search-modal"))closeSearch()};$("mobile-toggle").onclick=()=>$("sidebar").classList.toggle("open");window.onpopstate=()=>void navigate();
|
|
3408
|
-
|
|
3707
|
+
$("new-run").onclick=()=>openRunDrawer();$("close-drawer").onclick=closeRunDrawer;$("cancel-run").onclick=closeRunDrawer;$("drawer").onclick=e=>{if(e.target===$("drawer"))closeRunDrawer()};$("run-definition").onchange=()=>updateInstanceOptions();$("run-form").onsubmit=startRun;$("open-search").onclick=openSearch;$("search-input").oninput=renderSearch;$("search-modal").onclick=e=>{if(e.target===$("search-modal"))closeSearch()};$("mobile-toggle").onclick=()=>$("sidebar").classList.toggle("open");window.onpopstate=()=>void navigate();
|
|
3708
|
+
$("run-message").addEventListener("keydown",e=>{if((e.metaKey||e.ctrlKey)&&e.key==="Enter"){e.preventDefault();$("run-form").requestSubmit()}});
|
|
3709
|
+
document.addEventListener("keydown",e=>{
|
|
3710
|
+
if((e.metaKey||e.ctrlKey)&&e.key.toLowerCase()==="k"){e.preventDefault();openSearch();return}
|
|
3711
|
+
if(e.key==="Escape"){closeSearch();closeRunDrawer();return}
|
|
3712
|
+
if(e.metaKey||e.ctrlKey||e.altKey||isTyping())return;
|
|
3713
|
+
if($("drawer").classList.contains("open")||$("search-modal").classList.contains("open"))return;
|
|
3714
|
+
if(e.key==="j"){e.preventDefault();moveCursor(1)}
|
|
3715
|
+
else if(e.key==="k"){e.preventDefault();moveCursor(-1)}
|
|
3716
|
+
else if(e.key==="Enter"||e.key==="o"){if(state.cursor>=0){e.preventDefault();openCursor()}}
|
|
3717
|
+
else if(e.key==="/"){const box=$("run-search");if(box){e.preventDefault();box.focus();box.select()}else{e.preventDefault();openSearch()}}
|
|
3718
|
+
else if(e.key==="c"){e.preventDefault();openRunDrawer()}
|
|
3719
|
+
else if(e.key==="r"){e.preventDefault();void refreshActivity()}
|
|
3720
|
+
});
|
|
3721
|
+
setInterval(tickTimes,1000);
|
|
3722
|
+
loadBase().then(async()=>{
|
|
3723
|
+
state.ready=true;
|
|
3724
|
+
await navigate();
|
|
3725
|
+
const stream=new EventSource("/api/events");
|
|
3726
|
+
stream.onopen=()=>{$("event-state").textContent="Live updates"};
|
|
3727
|
+
stream.onmessage=scheduleRefresh;
|
|
3728
|
+
stream.onerror=()=>{$("event-state").textContent="Reconnecting…"};
|
|
3729
|
+
setInterval(()=>void refreshActivity(),10000);
|
|
3730
|
+
}).catch(error=>{$("content").innerHTML='<div class="form-error"><strong>Could not open Foundry.</strong><br>'+esc(error.message)+'</div>';$("runtime-state").classList.add("bad");$("runtime-state").querySelector("span").textContent="Runtime unavailable"});
|
|
3409
3731
|
`;
|
|
3410
3732
|
|
|
3411
3733
|
// src/dashboard-styles.ts
|
|
@@ -3441,14 +3763,119 @@ button,input,textarea,select{font:inherit;color:inherit}button,a{touch-action:ma
|
|
|
3441
3763
|
.status{display:inline-flex;align-items:center;gap:6px;border-radius:999px;padding:4px 8px;background:var(--bg-surface-2);color:var(--text-secondary);font:8px var(--mono);text-transform:uppercase;letter-spacing:.06em;white-space:nowrap;border:1px solid var(--border)}.status:before{content:"";width:5px;height:5px;border-radius:50%;background:var(--text-tertiary)}.status.completed,.status.connected,.status.active,.status.enabled,.status.resolved{background:rgba(140,196,148,.08);color:var(--success);border-color:rgba(140,196,148,.18)}.status.completed:before,.status.connected:before,.status.active:before,.status.enabled:before,.status.resolved:before{background:var(--success)}.status.running,.status.pending,.status.connecting,.status.reconnecting,.status.in-progress{background:rgba(131,179,226,.08);color:var(--network);border-color:rgba(131,179,226,.18)}.status.running:before,.status.pending:before,.status.connecting:before,.status.reconnecting:before,.status.in-progress:before{background:var(--network)}.status.failed,.status.cancelled,.status.disconnected,.status.dismissed{background:rgba(226,155,168,.08);color:var(--danger);border-color:rgba(226,155,168,.18)}.status.failed:before,.status.cancelled:before,.status.disconnected:before,.status.dismissed:before{background:var(--danger)}
|
|
3442
3764
|
.tag{display:inline-block;border:1px solid var(--border);border-radius:4px;padding:3px 6px;font:8px var(--mono);color:var(--text-secondary);margin:2px 4px 2px 0;background:var(--bg-surface)}.link{color:var(--accent);text-decoration:none;font-weight:500}.link:hover{text-decoration:underline;text-decoration-color:var(--accent-line)}.inline-icon{display:inline-flex;align-items:center;gap:5px}.inline-icon .icon{width:12px;height:12px}.empty{padding:38px 22px;text-align:center;color:var(--text-secondary);font-size:12px;line-height:1.6}.empty strong{display:block;color:var(--text-primary);font-size:14px;font-weight:500;margin-bottom:5px}.filters{display:flex;gap:8px;margin-bottom:14px}.filters input,.filters select{background:var(--bg-elevated);border:1px solid var(--border);border-radius:7px;padding:10px 12px;font-size:12px}.filters input{min-width:280px}
|
|
3443
3765
|
.definition-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px;margin-bottom:16px}.definition-card{display:block;color:inherit;text-decoration:none;background:var(--bg-elevated);border:1px solid var(--border-subtle);border-radius:var(--radius-lg);padding:19px;min-height:196px;position:relative;overflow:hidden;transition:transform .2s ease,border-color .2s ease,background .2s}.definition-card:before{content:"";position:absolute;inset:0;opacity:0;background:radial-gradient(110% 80% at 100% 0%,var(--accent-dim),transparent 60%);transition:opacity .2s}.definition-card:hover{transform:translateY(-2px);border-color:var(--accent-line);background:var(--bg-surface)}.definition-card:hover:before{opacity:1}.definition-icon{width:31px;height:31px;display:grid;place-items:center;border:1px solid var(--accent-line);border-radius:8px;color:var(--accent);background:var(--accent-dim);margin-bottom:15px;position:relative}.definition-icon .icon{width:17px;height:17px}.definition-open{position:absolute;right:17px;top:17px;color:var(--text-tertiary);transition:color .18s,transform .18s}.definition-open .icon{width:14px;height:14px}.definition-card:hover .definition-open{color:var(--accent);transform:translate(1px,-1px)}.definition-card h3{margin:0;font-size:17px;font-weight:500;position:relative}.definition-card p{color:var(--text-secondary);font-size:12px;font-weight:300;line-height:1.6;margin:9px 0 18px;position:relative}.definition-meta{position:absolute;left:19px;right:19px;bottom:17px;display:flex;justify-content:space-between;align-items:end}.definition-meta b{font:9px var(--mono);color:var(--accent-soft)}
|
|
3444
|
-
.cap-list{display:flex;flex-wrap:wrap;gap:6px}.cap{border:1px solid var(--border);background:var(--bg-surface);border-radius:5px;padding:7px 9px;font:9px var(--mono);margin-bottom:6px;color:var(--text-secondary)}.detail-strip{display:grid;grid-template-columns:repeat(
|
|
3766
|
+
.cap-list{display:flex;flex-wrap:wrap;gap:6px}.cap{border:1px solid var(--border);background:var(--bg-surface);border-radius:5px;padding:7px 9px;font:9px var(--mono);margin-bottom:6px;color:var(--text-secondary)}.detail-strip{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));border:1px solid var(--border-subtle);border-radius:var(--radius);background:var(--bg-elevated);margin-bottom:14px;overflow:hidden}.detail-strip>div{padding:15px;border-right:1px solid var(--border-subtle);min-width:0}.detail-strip>div:last-child{border-right:0}.detail-strip label{display:block;color:var(--text-tertiary);font:8px var(--mono);text-transform:uppercase;letter-spacing:.1em}.detail-strip strong{display:block;margin-top:8px;font-size:12px;font-weight:500;overflow-wrap:anywhere}.json{margin:0;background:#070A09;color:var(--text-secondary);border:1px solid var(--border-subtle);border-radius:7px;padding:14px;font:9px/1.65 var(--mono);white-space:pre-wrap;overflow:auto;max-height:360px}.summary-box{font-size:12px;line-height:1.65;overflow-wrap:anywhere}.summary-box dl{display:grid;grid-template-columns:130px 1fr;gap:9px 14px;margin:0}.summary-box dt{color:var(--text-tertiary);font:8px var(--mono);text-transform:uppercase;letter-spacing:.08em}.summary-box dd{margin:0;color:var(--text-secondary)}
|
|
3445
3767
|
.run-spine{position:relative;padding:4px 0}.run-spine:before{content:"";position:absolute;left:19px;top:14px;bottom:14px;width:1px;background:var(--border)}.phase{position:relative;display:grid;grid-template-columns:40px 1fr auto;gap:12px;padding:11px 4px}.phase-dot{width:12px;height:12px;border-radius:50%;background:var(--bg-elevated);border:2px solid var(--text-tertiary);margin:2px 0 0 13px;z-index:1}.phase.complete .phase-dot{border-color:var(--success);box-shadow:0 0 8px rgba(140,196,148,.2)}.phase.active .phase-dot{border-color:var(--network);box-shadow:0 0 0 5px rgba(131,179,226,.09)}.phase.error .phase-dot{border-color:var(--danger)}.phase strong{font-size:12px;font-weight:500}.phase p{margin:4px 0 0;color:var(--text-secondary);font-size:11px;font-weight:300;line-height:1.5}.phase time{font:8px var(--mono);color:var(--text-tertiary)}
|
|
3446
3768
|
.event-row{border-top:1px solid var(--border-subtle)}.event-toggle{width:100%;border:0;background:transparent;padding:12px 15px;display:grid;grid-template-columns:82px 110px minmax(0,1fr) auto;gap:11px;text-align:left;align-items:center;cursor:pointer}.event-toggle:hover,.event-row.open .event-toggle{background:var(--accent-glow)}.event-time,.event-category{font:8px var(--mono);color:var(--text-tertiary)}.event-type{font:9px var(--mono);overflow:hidden;text-overflow:ellipsis}.event-detail{display:none;padding:0 15px 14px}.event-row.open .event-detail{display:block}.trace-note{border-left:2px solid var(--accent);padding:10px 12px;background:var(--accent-dim);font-size:11px;line-height:1.5;color:var(--text-secondary);margin-bottom:14px}
|
|
3447
3769
|
.workspace-tabs{display:flex;gap:2px;border-bottom:1px solid var(--border-subtle);margin:20px 0 14px}.workspace-tabs button{border:0;background:transparent;padding:11px 13px;color:var(--text-secondary);font-size:11px;cursor:pointer;border-bottom:2px solid transparent}.workspace-tabs button.active{color:var(--accent);border-color:var(--accent);font-weight:500}.kv{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}.kv-item{border:1px solid var(--border-subtle);border-radius:7px;padding:13px;background:var(--bg-surface)}.kv-item strong{font:10px var(--mono)}.kv-item small{display:block;color:var(--text-tertiary);margin-top:6px}.kv-value{margin-top:12px;color:var(--text-secondary);font-size:11px;line-height:1.5;overflow-wrap:anywhere}.callout{border:1px solid var(--accent-line);background:var(--accent-glow);border-radius:8px;padding:15px 17px;display:flex;align-items:flex-start;gap:12px;margin-bottom:14px}.callout b{font-size:12px;font-weight:500}.callout p{margin:4px 0 0;color:var(--text-secondary);font-size:11px;line-height:1.5}.callout .symbol{width:31px;height:31px;display:grid;place-items:center;flex:0 0 auto;border:1px solid var(--accent-line);border-radius:7px;color:var(--accent);background:var(--accent-dim)}.callout .symbol .icon{width:16px;height:16px}
|
|
3448
3770
|
.drawer-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.62);backdrop-filter:blur(3px);z-index:40;display:none}.drawer-backdrop.open{display:block}.drawer{position:absolute;right:0;top:0;bottom:0;width:min(520px,100%);background:var(--bg-elevated);border-left:1px solid var(--border);box-shadow:-20px 0 60px rgba(0,0,0,.35);padding:26px;overflow:auto}.drawer-head{display:flex;align-items:flex-start;margin-bottom:28px}.drawer h2{margin:0;font-size:23px;font-weight:400;letter-spacing:-.025em}.drawer-head button{margin-left:auto;border:0;background:transparent;color:var(--text-secondary);cursor:pointer}.drawer-head button:hover{color:var(--text-primary);background:var(--bg-surface)}.field{margin-bottom:17px}.field label{display:block;font:8px var(--mono);text-transform:uppercase;letter-spacing:.12em;color:var(--text-tertiary);margin-bottom:7px}.field input,.field textarea,.field select{width:100%;border:1px solid var(--border);background:var(--bg);border-radius:7px;padding:11px 12px;font-size:12px}.field textarea{min-height:120px;resize:vertical}.form-error{background:rgba(226,155,168,.08);color:var(--danger);border:1px solid rgba(226,155,168,.18);border-radius:7px;padding:10px 12px;font-size:11px;margin-bottom:13px}.drawer-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:22px}
|
|
3449
3771
|
.search-modal{position:fixed;inset:0;background:rgba(0,0,0,.64);backdrop-filter:blur(4px);z-index:50;display:none;padding:12vh 20px}.search-modal.open{display:block}.search-box{max-width:650px;margin:auto;background:var(--bg-elevated);border:1px solid var(--border);border-radius:10px;box-shadow:var(--shadow);overflow:hidden}.search-input-wrap{display:flex;align-items:center;gap:12px;padding:0 20px;border-bottom:1px solid var(--border);color:var(--text-tertiary)}.search-input-wrap>.icon{width:19px;height:19px}.search-box input{width:100%;border:0;background:var(--bg-elevated);padding:18px 0;font-size:15px}.search-results{max-height:420px;overflow:auto;padding:8px}.search-result{display:block;padding:11px 12px;border-radius:6px;text-decoration:none;color:inherit}.search-result:hover{background:var(--accent-glow)}.search-result strong{font-size:12px;font-weight:500}.search-result small{display:block;color:var(--text-tertiary);margin-top:3px}.toast{position:fixed;right:24px;bottom:24px;background:var(--accent);color:var(--bg);border-radius:7px;padding:12px 15px;font-size:11px;font-weight:600;box-shadow:var(--shadow);z-index:60;transform:translateY(20px);opacity:0;pointer-events:none;transition:.2s}.toast.show{transform:none;opacity:1}
|
|
3450
|
-
@media(max-width:1100px){.cols-4{grid-template-columns:repeat(2,1fr)}.definition-grid{grid-template-columns:repeat(2,1fr)}.cols-3{grid-template-columns:1fr 1fr}
|
|
3772
|
+
@media(max-width:1100px){.cols-4{grid-template-columns:repeat(2,1fr)}.definition-grid{grid-template-columns:repeat(2,1fr)}.cols-3{grid-template-columns:1fr 1fr}}
|
|
3451
3773
|
@media(max-width:760px){:root{--sidebar:0px}.sidebar{transform:translateX(-248px);width:248px;transition:transform .2s ease}.sidebar.open{transform:none}.mobile-toggle{display:inline-flex}.topbar{padding:0 16px}.live-chip{display:none}.content{padding:24px 16px 55px}.page-head{display:block}.page-head .actions{margin:18px 0 0}.cols-4,.cols-3,.cols-2,.definition-grid,.kv{grid-template-columns:1fr}.span-2{grid-column:auto}.detail-strip{display:block}.detail-strip>div{border-right:0;border-bottom:1px solid var(--border-subtle)}.filters{flex-wrap:wrap}.filters input{min-width:100%;width:100%}.event-toggle{grid-template-columns:70px 90px 1fr}.event-toggle span:last-child{display:none}.top-actions .button.primary span{display:none}}
|
|
3774
|
+
|
|
3775
|
+
/* ---- Elapsed time and duration ---- */
|
|
3776
|
+
.rel{color:var(--text-secondary);white-space:nowrap;font-variant-numeric:tabular-nums;cursor:help;border-bottom:1px dotted var(--border-strong)}
|
|
3777
|
+
.dur{font-family:var(--mono);font-size:10px;color:var(--text-secondary);white-space:nowrap;font-variant-numeric:tabular-nums}
|
|
3778
|
+
.dur.live{color:var(--accent);position:relative;padding-left:12px}
|
|
3779
|
+
.dur.live:before{content:"";position:absolute;left:0;top:50%;transform:translateY(-50%);width:5px;height:5px;border-radius:50%;background:var(--accent);animation:pulse-dot 1.6s ease-in-out infinite}
|
|
3780
|
+
@keyframes pulse-dot{0%,100%{opacity:1;box-shadow:0 0 0 0 var(--accent-line)}50%{opacity:.45;box-shadow:0 0 0 4px transparent}}
|
|
3781
|
+
.attempts{font-family:var(--mono);font-size:10px;color:var(--text-tertiary);font-variant-numeric:tabular-nums}
|
|
3782
|
+
.attempts.warn{color:var(--warn)}
|
|
3783
|
+
|
|
3784
|
+
/* ---- Copyable identifiers ---- */
|
|
3785
|
+
.id-cell{display:inline-flex;align-items:center;gap:5px;min-width:0}
|
|
3786
|
+
.id-cell .mono{font-family:var(--mono);font-size:11px;color:var(--text-primary);overflow:hidden;text-overflow:ellipsis}
|
|
3787
|
+
.copy-btn{border:0;background:transparent;color:var(--text-tertiary);padding:3px;border-radius:5px;cursor:pointer;display:inline-flex;align-items:center;opacity:0;transition:opacity .14s,color .14s,background .14s}
|
|
3788
|
+
.copy-btn .icon{width:12px;height:12px}
|
|
3789
|
+
.copy-btn .copy-ok{display:none}
|
|
3790
|
+
tr:hover .copy-btn,.id-cell:hover .copy-btn,.copy-btn:focus-visible{opacity:1}
|
|
3791
|
+
.copy-btn:hover{color:var(--accent);background:var(--accent-dim)}
|
|
3792
|
+
.copy-btn.copied{opacity:1;color:var(--success)}
|
|
3793
|
+
.copy-btn.copied .icon{display:none}
|
|
3794
|
+
.copy-btn.copied .copy-ok{display:block}
|
|
3795
|
+
.button .icon{width:13px;height:13px}
|
|
3796
|
+
|
|
3797
|
+
/* ---- Filter toolbar ---- */
|
|
3798
|
+
.toolbar{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:14px}
|
|
3799
|
+
.toolbar-right{display:flex;align-items:center;gap:8px;margin-left:auto;flex-wrap:wrap}
|
|
3800
|
+
.segmented{display:inline-flex;background:var(--bg-elevated);border:1px solid var(--border-subtle);border-radius:8px;padding:3px;gap:2px;overflow:auto}
|
|
3801
|
+
.segmented button{border:0;background:transparent;color:var(--text-secondary);font-size:11px;font-weight:500;padding:6px 10px;border-radius:6px;cursor:pointer;display:inline-flex;align-items:center;gap:6px;white-space:nowrap;transition:background .16s,color .16s}
|
|
3802
|
+
.segmented button:hover{color:var(--text-primary)}
|
|
3803
|
+
.segmented button.active{background:var(--bg-surface-2);color:var(--text-primary);box-shadow:inset 0 0 0 1px var(--border)}
|
|
3804
|
+
.seg-count{font-family:var(--mono);font-size:9px;color:var(--text-tertiary);background:var(--bg);border-radius:20px;padding:2px 6px;font-variant-numeric:tabular-nums}
|
|
3805
|
+
.segmented button.active .seg-count{color:var(--accent);background:var(--accent-dim)}
|
|
3806
|
+
.search-field,.select-field{position:relative;display:inline-flex;align-items:center}
|
|
3807
|
+
.search-field-icon,.select-field-icon{position:absolute;left:10px;display:flex;color:var(--text-tertiary);pointer-events:none}
|
|
3808
|
+
.search-field-icon .icon,.select-field-icon .icon{width:13px;height:13px}
|
|
3809
|
+
.search-field input,.select-field select{background:var(--bg-elevated);border:1px solid var(--border-subtle);border-radius:8px;padding:8px 12px 8px 30px;font-size:12px;color:var(--text-primary);min-width:150px}
|
|
3810
|
+
.search-field input{min-width:230px}
|
|
3811
|
+
.select-field select{appearance:none;cursor:pointer;padding-right:26px}
|
|
3812
|
+
.search-field input::placeholder{color:var(--text-tertiary)}
|
|
3813
|
+
.search-field input:focus,.select-field select:focus{border-color:var(--border-strong);outline:none}
|
|
3814
|
+
.filter-note{display:flex;align-items:center;gap:12px;flex-wrap:wrap;font-size:11px;color:var(--text-secondary);background:var(--accent-glow);border:1px solid var(--accent-line);border-radius:8px;padding:9px 13px;margin-bottom:14px}
|
|
3815
|
+
.filter-note .link{border:0;background:transparent;cursor:pointer;font-size:11px;padding:0;margin-left:auto}
|
|
3816
|
+
.hint{font:10px var(--mono);color:var(--text-tertiary);margin:14px 0 0;letter-spacing:.03em}
|
|
3817
|
+
.card-foot{padding:11px 15px;border-top:1px solid var(--border-subtle);font-size:11px}
|
|
3818
|
+
|
|
3819
|
+
/* ---- Row states and the keyboard cursor ---- */
|
|
3820
|
+
.runs-table{min-width:820px}
|
|
3821
|
+
tr.row-failed td:first-child{box-shadow:inset 2px 0 0 var(--danger)}
|
|
3822
|
+
tr.row-live td:first-child{box-shadow:inset 2px 0 0 var(--accent)}
|
|
3823
|
+
tr.cursor,.definition-card.cursor{outline:1px solid var(--accent-soft);outline-offset:-1px;background:var(--accent-glow)}
|
|
3824
|
+
|
|
3825
|
+
/* ---- Alerts ---- */
|
|
3826
|
+
.alert{display:flex;gap:13px;align-items:flex-start;border-radius:var(--radius);padding:14px 16px;margin-bottom:16px;border:1px solid var(--border)}
|
|
3827
|
+
.alert .symbol{display:flex;flex:0 0 auto;margin-top:1px}
|
|
3828
|
+
.alert .symbol .icon{width:17px;height:17px}
|
|
3829
|
+
.alert b{display:block;font-size:12px;font-weight:600;margin-bottom:3px}
|
|
3830
|
+
.alert p{margin:0;font-size:12px;color:var(--text-secondary);line-height:1.55;word-break:break-word}
|
|
3831
|
+
.alert.danger{background:rgba(226,155,168,.07);border-color:rgba(226,155,168,.26)}
|
|
3832
|
+
.alert.danger .symbol{color:var(--danger)}
|
|
3833
|
+
.alert.danger b{color:var(--danger)}
|
|
3834
|
+
.alert.live{background:var(--accent-glow);border-color:var(--accent-line)}
|
|
3835
|
+
.alert.live b{color:var(--accent)}
|
|
3836
|
+
.pulse-dot{width:9px;height:9px;border-radius:50%;background:var(--accent);display:block;margin-top:4px;animation:pulse-dot 1.6s ease-in-out infinite}
|
|
3837
|
+
.live-chip.pulse i{animation:live-ping .7s ease-out}
|
|
3838
|
+
@keyframes live-ping{0%{transform:scale(1);opacity:1}45%{transform:scale(2.1);opacity:.45}100%{transform:scale(1);opacity:1}}
|
|
3839
|
+
|
|
3840
|
+
/* ---- Event trace ---- */
|
|
3841
|
+
.event-controls{display:flex;align-items:center;gap:12px;flex-wrap:wrap;padding:12px 15px;border-bottom:1px solid var(--border-subtle)}
|
|
3842
|
+
.event-controls-right{display:flex;align-items:center;gap:12px;margin-left:auto;flex-wrap:wrap}
|
|
3843
|
+
.event-controls-right .muted{font-size:11px}
|
|
3844
|
+
.event-list{max-height:640px;overflow:auto}
|
|
3845
|
+
.chips{display:flex;gap:5px;flex-wrap:wrap}
|
|
3846
|
+
.chip{border:1px solid var(--border-subtle);background:var(--bg-elevated);color:var(--text-secondary);border-radius:20px;padding:5px 11px;font:9px var(--mono);text-transform:uppercase;letter-spacing:.07em;cursor:pointer;transition:border-color .16s,color .16s,background .16s}
|
|
3847
|
+
.chip:hover{color:var(--text-primary);border-color:var(--border)}
|
|
3848
|
+
.chip.active{background:var(--accent-dim);border-color:var(--accent-line);color:var(--accent)}
|
|
3849
|
+
.event-toggle{grid-template-columns:74px 62px 104px minmax(0,1fr) auto!important}
|
|
3850
|
+
.event-offset{color:var(--accent-soft);font-size:9px;font-variant-numeric:tabular-nums}
|
|
3851
|
+
.event-caret{display:flex;color:var(--text-tertiary)}
|
|
3852
|
+
.event-caret .icon{width:13px;height:13px;transition:transform .18s}
|
|
3853
|
+
.event-row.open .event-caret .icon{transform:rotate(180deg)}
|
|
3854
|
+
.event-detail-head{display:flex;align-items:center;gap:8px;padding:9px 15px 0}
|
|
3855
|
+
.event-detail-head .copy-btn{opacity:1;margin-left:auto}
|
|
3856
|
+
|
|
3857
|
+
/* ---- Skeletons ---- */
|
|
3858
|
+
.skeleton{padding:6px 0}
|
|
3859
|
+
.skeleton-row{display:flex;align-items:center;gap:16px;padding:13px 15px;border-bottom:1px solid var(--border-subtle)}
|
|
3860
|
+
.skeleton-row span{height:9px;border-radius:20px;background:linear-gradient(90deg,var(--bg-surface) 0%,var(--bg-surface-2) 50%,var(--bg-surface) 100%);background-size:200% 100%;animation:shimmer 1.5s linear infinite}
|
|
3861
|
+
.skeleton-row span:last-child{margin-left:auto}
|
|
3862
|
+
@keyframes shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}
|
|
3863
|
+
.boot-skeleton{padding:32px}
|
|
3864
|
+
.boot-skeleton p{font-size:12px;margin-top:18px}
|
|
3865
|
+
|
|
3866
|
+
/* ---- Run drawer ---- */
|
|
3867
|
+
.drawer-hint{font-size:9px;color:var(--text-tertiary);margin-right:auto;letter-spacing:.06em}
|
|
3868
|
+
.button.pending{opacity:.65;cursor:progress}
|
|
3869
|
+
.button.danger{color:var(--danger);border-color:rgba(226,155,168,.3)}
|
|
3870
|
+
.button.danger:hover{background:rgba(226,155,168,.09);border-color:var(--danger)}
|
|
3871
|
+
|
|
3872
|
+
@media(max-width:760px){
|
|
3873
|
+
.toolbar-right{margin-left:0;width:100%}
|
|
3874
|
+
.search-field,.search-field input{width:100%;min-width:0}
|
|
3875
|
+
.event-toggle{grid-template-columns:60px 54px minmax(0,1fr) auto!important}
|
|
3876
|
+
.event-toggle .event-category{display:none}
|
|
3877
|
+
.hint{display:none}
|
|
3878
|
+
}
|
|
3452
3879
|
@media(prefers-reduced-motion:reduce){*{scroll-behavior:auto!important;transition:none!important}}
|
|
3453
3880
|
`;
|
|
3454
3881
|
|
|
@@ -3497,12 +3924,12 @@ function renderDashboard() {
|
|
|
3497
3924
|
<button class="button icon-button mobile-toggle" id="mobile-toggle" aria-label="Open navigation">${renderPhosphorIcon("menu")}</button>
|
|
3498
3925
|
<div class="breadcrumbs" id="breadcrumbs"></div>
|
|
3499
3926
|
<div class="top-actions">
|
|
3500
|
-
<span class="live-chip"><i></i><span id="event-state">
|
|
3927
|
+
<span class="live-chip" title="The inspector streams runtime events and reconciles on a low-frequency timer."><i></i><span id="event-state">Connecting\u2026</span></span>
|
|
3501
3928
|
<button class="button primary" id="new-run">${renderPhosphorIcon("plus")} <span>New run</span></button>
|
|
3502
3929
|
</div>
|
|
3503
3930
|
</header>
|
|
3504
3931
|
<main class="content" id="content">
|
|
3505
|
-
<div class="
|
|
3932
|
+
<div class="boot-skeleton"><div class="skeleton" aria-busy="true" aria-label="Opening the Foundry"><div class="skeleton-row"><span style="width:38%"></span><span style="width:14%"></span></div><div class="skeleton-row"><span style="width:62%"></span><span style="width:18%"></span></div><div class="skeleton-row"><span style="width:51%"></span><span style="width:11%"></span></div><div class="skeleton-row"><span style="width:70%"></span><span style="width:16%"></span></div></div><p class="muted">Reading definitions, instances, and runtime activity\u2026</p></div>
|
|
3506
3933
|
</main>
|
|
3507
3934
|
</section>
|
|
3508
3935
|
</div>
|
|
@@ -3518,7 +3945,7 @@ function renderDashboard() {
|
|
|
3518
3945
|
<div class="field"><label for="run-definition">Agent definition</label><select id="run-definition"></select></div>
|
|
3519
3946
|
<div class="field"><label for="run-instance">Runtime instance</label><select id="run-instance"></select><small class="muted">Choose \u201CCreate a new instance\u201D to provision one.</small></div>
|
|
3520
3947
|
<div class="field"><label for="run-message">Message</label><textarea id="run-message" required placeholder="What should this agent work on?"></textarea></div>
|
|
3521
|
-
<div class="drawer-actions"><button type="button" class="button" id="cancel-run">Cancel</button><button type="submit" class="button primary">Start run</button></div>
|
|
3948
|
+
<div class="drawer-actions"><span class="drawer-hint mono">\u2318 + Enter</span><button type="button" class="button" id="cancel-run">Cancel</button><button type="submit" class="button primary" id="run-submit">Start run</button></div>
|
|
3522
3949
|
</form>
|
|
3523
3950
|
</section>
|
|
3524
3951
|
</div>
|
|
@@ -3709,7 +4136,17 @@ var FoundryServer = class {
|
|
|
3709
4136
|
response.writeHead(200, {
|
|
3710
4137
|
"content-type": "text/html; charset=utf-8",
|
|
3711
4138
|
"cache-control": "no-store",
|
|
3712
|
-
|
|
4139
|
+
// The inspector's stylesheet pulls the Glove brand typefaces from
|
|
4140
|
+
// Google Fonts, so style-src and font-src name those two origins and
|
|
4141
|
+
// nothing else. Everything executable stays same-origin.
|
|
4142
|
+
"content-security-policy": [
|
|
4143
|
+
"default-src 'self'",
|
|
4144
|
+
"script-src 'unsafe-inline' 'self'",
|
|
4145
|
+
"style-src 'unsafe-inline' 'self' https://fonts.googleapis.com",
|
|
4146
|
+
"font-src 'self' https://fonts.gstatic.com",
|
|
4147
|
+
"img-src 'self' data:",
|
|
4148
|
+
"connect-src 'self'"
|
|
4149
|
+
].join("; "),
|
|
3713
4150
|
"x-content-type-options": "nosniff",
|
|
3714
4151
|
"x-frame-options": "DENY"
|
|
3715
4152
|
});
|