agents-relay 2.0.180 → 2.0.182
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/dashboard-html.js +11 -10
- package/dist/relayd.js +10 -4
- package/package.json +1 -1
package/dist/dashboard-html.js
CHANGED
|
@@ -219,7 +219,7 @@ function copySelectedJobIdentity(job){if(!job||!navigator.clipboard?.writeText)r
|
|
|
219
219
|
function selectJob(job){selectedJob=jobSelectionKey(job);selectedTaskId=null;eventTaskId=null;rememberSelection();clearEvents();location.hash=selectedJob?'#/jobs/'+encodeURIComponent(selectedJob):'#/jobs';copySelectedJobIdentity(job);renderAll();void loadEventHistory();}
|
|
220
220
|
function matchesQuickFilter(job){return !selectedStatus?${JSON.stringify(DEFAULT_DASHBOARD_JOB_STATUSES)}.some(status=>status==='DRAFT'?job.draft||job.state==='DRAFT':job.state===status):selectedStatus==='DRAFT'?job.draft||job.state==='DRAFT':job.state===selectedStatus;}
|
|
221
221
|
function matchesSearch(job){const query=searchQuery.trim().toLowerCase();if(!query)return true;const pr=String(job.prNumber||'');const normalized=query.replace(/^pr\s*#?/,'').replace(/^#/,'');return String(job.title||job.id||'').toLowerCase().includes(query)||String(job.id||'').toLowerCase().includes(query)||shortManagedId(job.id,job.repository,'j').toLowerCase().includes(query)||pr.includes(normalized);}
|
|
222
|
-
function filteredJobs(){return jobs.filter(job=>(!selectedRepo||selectedRepo===job.repository)&&matchesQuickFilter(job)&&matchesSearch(job));}
|
|
222
|
+
function filteredJobs(){return jobs.filter(job=>(!selectedRepo||selectedRepo===job.repository)&&matchesQuickFilter(job)&&matchesSearch(job)).slice(0,20);}
|
|
223
223
|
function syncFilters(){
|
|
224
224
|
const repos=jobRepositories.length?jobRepositories:[...new Set(jobs.map(job=>job.repository).filter(Boolean))].sort();
|
|
225
225
|
const repoOptions=[new Option('All repositories',''),...repos.map(repo=>new Option(repo,repo))];
|
|
@@ -228,7 +228,7 @@ function syncFilters(){
|
|
|
228
228
|
if(!['','DRAFT','ACTIVE','COMPLETED','CANCELLED'].includes(selectedStatus))selectedStatus='';
|
|
229
229
|
statusFilter.value=selectedStatus;
|
|
230
230
|
repoFilter.value=selectedRepo;
|
|
231
|
-
jobSearch.value=searchQuery;
|
|
231
|
+
if(jobSearch.value!==searchQuery)jobSearch.value=searchQuery;
|
|
232
232
|
}
|
|
233
233
|
function repoName(repository){if(typeof repository!=='string'||!repository)return 'unknown-repo';const parts=repository.split('/').filter(Boolean);return parts.at(-1)||repository;}
|
|
234
234
|
const CHAT_SHORTCUTS_KEY='agents-relay:chatgpt-shortcuts';
|
|
@@ -276,7 +276,7 @@ async function refreshStatus(){try{renderRateLimitStatus(await loadJSON('/api/st
|
|
|
276
276
|
function rememberJobFilters(){sessionStorage.setItem('agents-relay:repo-filter',selectedRepo);sessionStorage.setItem('agents-relay:status-filter',selectedStatus);sessionStorage.setItem('agents-relay:job-search',searchQuery);}
|
|
277
277
|
function jobsQuery(force=false){const params=new URLSearchParams();if(searchQuery.trim())params.set('q',searchQuery.trim());if(selectedStatus)params.set('status',selectedStatus);if(selectedRepo)params.set('repo',selectedRepo);if(force)params.set('refresh','1');return params.toString();}
|
|
278
278
|
let jobsRequestSequence=0;
|
|
279
|
-
async function refresh(force=true){const requestSequence=++jobsRequestSequence;jobsRefresh.disabled=true;
|
|
279
|
+
async function refresh(force=true){const requestSequence=++jobsRequestSequence;jobsRefresh.disabled=true;jobsFeedback.textContent='';try{const params=jobsQuery(force);const discovered=await loadJSON('/api/jobs'+(params?'?'+params:''));if(requestSequence!==jobsRequestSequence)return;const result=Array.isArray(discovered)?{jobs:discovered,repositories:[...new Set(discovered.map(job=>job.repository).filter(Boolean))].sort()}:discovered;jobs=dedupeJobs(Array.isArray(result.jobs)?result.jobs:[]).sort(compareJobs).slice(0,20);jobRepositories=result.repositories||[];void drawRuntimeSummary();jobs.sort(compareJobs);if(selectedJob&&!jobForSelection(selectedJob)){selectedJob=null;selectedTaskId=null;}if(!selectedJob){const match=location.hash.match(/^#\\/jobs\\/(.+)$/);const fromHash=match?decodeURIComponent(match[1]):null;const fromRoute=jobForSelection(fromHash);selectedJob=jobSelectionKey(fromRoute||jobs[0]);}rememberSelection();rememberJobFilters();renderAll();void loadEventHistory();jobsFeedback.textContent='';connection.textContent='';connection.setAttribute('aria-label','Live durable state connected');connection.className='status-pill ok';}catch(error){if(requestSequence!==jobsRequestSequence)return;jobsFeedback.textContent='Refresh failed: '+error.message;connection.textContent='';connection.setAttribute('aria-label','Durable state unavailable');connection.className='status-pill bad';jobList.replaceChildren(element('li','error-state',text('Could not load jobs: '+error.message)));}finally{if(requestSequence===jobsRequestSequence){jobsRefresh.disabled=false;syncFilters();}}}
|
|
280
280
|
function renderEventScope(){const task=eventTaskId&&selected()?.tasks?.find(candidate=>candidate.id===eventTaskId);eventsAllTasks.disabled=!task;eventsAllTasks.setAttribute('aria-pressed',String(!task));eventsScope.textContent=task?'Task '+task.id+' · newest first · 60 max':'Job-wide · newest first · 60 max';}
|
|
281
281
|
function eventLabel(type){return ({'task.process.launched':'Process launched','task.started':'Worker started','task.process.exited':'Sync process exited','task.process.async_exited':'Async process exited','task.completed':'Worker completed','task.failed':'Worker failed','task.blocked':'Worker blocked','review.started':'Review started','review.approved':'Review approved','review.changes_required':'Review changes required','review.blocked':'Review blocked'})[type]||type||'Event';}
|
|
282
282
|
function eventSeverity(event,level){const type=String(event?.type||'').toLowerCase(),eventLevel=String(event?.level||level||'').toLowerCase(),outcome=String(event?.outcome||event?.status||'').toLowerCase();if(eventLevel==='error'||outcome==='failed'||type.endsWith('.failed')||type==='task.failed')return 'error';if(eventLevel==='warn'||outcome==='blocked'||type.endsWith('.blocked')||type==='task.blocked'||type==='review.blocked')return 'warn';return 'normal';}
|
|
@@ -331,14 +331,15 @@ async function drawShortcuts(){shortcutsStatus.textContent='';shortcutsText.valu
|
|
|
331
331
|
function saveShortcuts(){const value=shortcutsText.value.trim();if(value)localStorage.setItem(CHAT_SHORTCUTS_KEY,value);else localStorage.removeItem(CHAT_SHORTCUTS_KEY);shortcutsText.value=chatShortcutText();shortcutsStatus.textContent='Saved.';}
|
|
332
332
|
async function drawQa(){qaStatus.textContent='Loading…';try{const result=await loadJSON('/api/troubleshooter-qa');qaText.value=result.text;qaRevision=result.hash;qaStatus.textContent='Loaded';}catch(error){qaStatus.textContent='Load failed: '+error.message;}}
|
|
333
333
|
async function saveQa(){qaSave.disabled=true;qaStatus.textContent='Saving…';try{const response=await fetch('/api/troubleshooter-qa',{method:'PUT',headers:{'content-type':'application/json'},body:JSON.stringify({text:qaText.value,hash:qaRevision})});if(!response.ok)throw new Error(response.status+' '+await response.text());const saved=await response.json();qaRevision=saved.hash;qaStatus.textContent='Saved to Markdown file';}catch(error){qaStatus.textContent='Save failed: '+error.message;}finally{qaSave.disabled=false;}}
|
|
334
|
-
function showView(view){const isJobs=view==='jobs';const isUsage=view==='usage';const isGraphs=view==='graphs';const isRules=view==='rules';const isShortcuts=view==='shortcuts';const isQa=view==='qa';qaView.hidden=!isQa;qaTab.setAttribute('aria-selected',String(isQa));jobsView.hidden=!isJobs;jobsWorkspace.hidden=!isJobs;usageView.hidden=!isUsage;graphsView.hidden=!isGraphs;rulesView.hidden=!isRules;shortcutsView.hidden=!isShortcuts;jobsTab.setAttribute('aria-selected',String(isJobs));usageTab.setAttribute('aria-selected',String(isUsage));graphsTab.setAttribute('aria-selected',String(isGraphs));rulesTab.setAttribute('aria-selected',String(isRules));shortcutsTab.setAttribute('aria-selected',String(isShortcuts));if(
|
|
334
|
+
function showView(view){const isJobs=view==='jobs';const isUsage=view==='usage';const isGraphs=view==='graphs';const isRules=view==='rules';const isShortcuts=view==='shortcuts';const isQa=view==='qa';qaView.hidden=!isQa;qaTab.setAttribute('aria-selected',String(isQa));jobsView.hidden=!isJobs;jobsWorkspace.hidden=!isJobs;usageView.hidden=!isUsage;graphsView.hidden=!isGraphs;rulesView.hidden=!isRules;shortcutsView.hidden=!isShortcuts;jobsTab.setAttribute('aria-selected',String(isJobs));usageTab.setAttribute('aria-selected',String(isUsage));graphsTab.setAttribute('aria-selected',String(isGraphs));rulesTab.setAttribute('aria-selected',String(isRules));shortcutsTab.setAttribute('aria-selected',String(isShortcuts));if(isUsage)void drawUsage();else if(isGraphs)void drawGraphs();else if(isRules)void drawRules();else if(isQa)void drawQa();else if(!isJobs)void drawShortcuts();}
|
|
335
335
|
jobsTab.addEventListener('click',()=>{location.hash=selectedJob?'#/jobs/'+encodeURIComponent(selectedJob):'#/jobs';showView('jobs');});
|
|
336
336
|
jobsRefresh.addEventListener('click',()=>void refresh(true));
|
|
337
337
|
eventsAllTasks.addEventListener('click',()=>{eventTaskId=null;clearEvents();renderEventScope();void loadEventHistory();});
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
338
|
+
function applyLocalJobFilters(){rememberJobFilters();renderJobList();}
|
|
339
|
+
jobsReset.addEventListener('click',()=>{searchQuery='';selectedStatus='';selectedRepo='';syncFilters();applyLocalJobFilters();});
|
|
340
|
+
repoFilter.addEventListener('change',()=>{selectedRepo=repoFilter.value;applyLocalJobFilters();});
|
|
341
|
+
jobSearch.addEventListener('input',()=>{searchQuery=jobSearch.value;applyLocalJobFilters();});
|
|
342
|
+
statusFilter.addEventListener('change',()=>{selectedStatus=statusFilter.value;applyLocalJobFilters();});
|
|
342
343
|
selectedJobRefresh.addEventListener('click',()=>void refreshSelected());
|
|
343
344
|
mobileJobsBack.addEventListener('click',()=>{location.hash='#/jobs';syncMobileJobsView();window.scrollTo({top:0,behavior:'instant'});});
|
|
344
345
|
mobileJobs.addEventListener('change',syncMobileJobsView);
|
|
@@ -370,10 +371,10 @@ stream.onopen=()=>{connection.textContent='';connection.setAttribute('aria-label
|
|
|
370
371
|
stream.onerror=()=>{connection.textContent='';connection.setAttribute('aria-label','Event stream reconnecting');connection.className='status-pill warn';};
|
|
371
372
|
stream.onmessage=message=>{const payload=parseMessage(message);if(payload?.job){upsertFreshJob(payload.job);renderAll();void drawRuntimeSummary();}};
|
|
372
373
|
stream.addEventListener('relay',message=>{const payload=parseMessage(message);if(payload?.job)upsertFreshJob(payload.job);else if(payload?.statePatch)applyStatePatch(payload.statePatch);if(payload?.event)addEvent(payload.event);renderAll();void drawRuntimeSummary();});
|
|
373
|
-
if(location.hash==='#/usage')showView('usage');else if(graphFromHash())showView('graphs');else if(location.hash==='#/execution-router-rules')showView('rules');else if(location.hash==='#/shortcuts')showView('shortcuts');else if(location.hash==='#/troubleshooter-qa')showView('qa');else showView('jobs');
|
|
374
|
+
if(location.hash==='#/usage')showView('usage');else if(graphFromHash())showView('graphs');else if(location.hash==='#/execution-router-rules')showView('rules');else if(location.hash==='#/shortcuts')showView('shortcuts');else if(location.hash==='#/troubleshooter-qa')showView('qa');else showView('jobs');void refresh(false);
|
|
374
375
|
setInterval(()=>{if(location.hash==='#/usage')void drawUsage();},30000);
|
|
375
376
|
setInterval(()=>void refreshVersion(),30000);
|
|
376
|
-
async function refreshSelected(){const job=selected();if(!job){await refresh();return;}selectedJobRefresh.disabled=true;
|
|
377
|
+
async function refreshSelected(){const job=selected();if(!job){await refresh();return;}selectedJobRefresh.disabled=true;try{const params=new URLSearchParams({id:job.id,repo:job.repository,pr:String(job.prNumber)});const updated=await loadJSON('/api/job?'+params.toString());const index=jobs.findIndex(candidate=>jobSelectionKey(candidate)===jobSelectionKey(job));if(index>=0)jobs[index]=updated;else jobs.push(updated);selectedJob=jobSelectionKey(updated);rememberSelection();renderAll();connection.textContent='';connection.setAttribute('aria-label','Live durable state connected');connection.className='status-pill ok';}catch(error){connection.textContent='';connection.setAttribute('aria-label','Durable state unavailable');connection.className='status-pill bad';}finally{selectedJobRefresh.disabled=false;}}
|
|
377
378
|
</script>
|
|
378
379
|
</body>
|
|
379
380
|
</html>`;
|
package/dist/relayd.js
CHANGED
|
@@ -137,7 +137,7 @@ export async function runDaemon(argv) {
|
|
|
137
137
|
}
|
|
138
138
|
if (repositories.length === 0)
|
|
139
139
|
repositories.push('local/' + basename(workspaceRoot));
|
|
140
|
-
const inventoryFromLocal = async () => {
|
|
140
|
+
const inventoryFromLocal = async (limitHistory = true) => {
|
|
141
141
|
const snapshots = await discoverLocalRelayStoreSnapshots(relayStoreDirectory());
|
|
142
142
|
const known = new Set(repositories);
|
|
143
143
|
const entries = snapshots.flatMap(snapshot => snapshot.jobs.filter(job => known.has(job.repository)).map(job => ({
|
|
@@ -150,7 +150,8 @@ export async function runDaemon(argv) {
|
|
|
150
150
|
counts.set(item.job.id, (counts.get(item.job.id) ?? 0) + 1);
|
|
151
151
|
// Hide obsolete terminal snapshots only when one live authoritative Job exists.
|
|
152
152
|
// Multiple live copies remain visible for diagnosis but cannot be mutated.
|
|
153
|
-
|
|
153
|
+
const visible = entries.filter(item => !['COMPLETED', 'CANCELLED'].includes(item.job.state) || counts.get(item.job.id) !== 1);
|
|
154
|
+
return limitHistory ? dashboardManagedJobs(visible) : visible;
|
|
154
155
|
};
|
|
155
156
|
cachedOverviews = await inventoryFromLocal();
|
|
156
157
|
const makeReconciler = (store, maxConcurrent) => new Reconciler(store, { noGitHub, owner: `relayd-${process.pid}`, maxConcurrent, leaseMs: Number(value(argv, '--lease-ms', '300000')), adapters: [new CodexAdapter(value(argv, '--codex', 'codex')), new ChatGptAdapter()], continuations: [new CodexThreadContinuation(value(argv, '--codex', 'codex')), new CommandContinuation(), new WebhookContinuation()], planner: runtimePlanner(argv), usageSnapshotProvider: () => usageRegistry.snapshot(), runtimeSummaryProvider: () => ({ chatgptRunning: summarizeRuntime(cachedOverviews.map(item => item.job)).workers.chatgpt }), decisionEngine: executionDecisionEngine(), decisionMode: decisionMode(), decisionAuditStore, eventBus: bus, escalationNotifier: escalationNotifierFromEnvironment(), reviewer: runtimeReviewer(argv), executionContextRoots: { workspaceRoot }, projectPathForJob: async (job, task) => {
|
|
@@ -176,7 +177,8 @@ export async function runDaemon(argv) {
|
|
|
176
177
|
cachedOverviews = await inventoryFromLocal();
|
|
177
178
|
return cachedOverviews;
|
|
178
179
|
};
|
|
179
|
-
|
|
180
|
+
// Search the full SQLite inventory; keep the executor's bounded view unchanged.
|
|
181
|
+
const overviews = async (_forceRefresh = false, _trigger = 'internal.unknown') => inventoryFromLocal(false);
|
|
180
182
|
const refreshPullRequestSnapshot = async (repository, prNumber, _trigger = 'internal', _options = {}) => {
|
|
181
183
|
const inventory = await refreshAll();
|
|
182
184
|
return {
|
|
@@ -429,6 +431,10 @@ export async function runDaemon(argv) {
|
|
|
429
431
|
} : undefined;
|
|
430
432
|
const exactJob = async (jobId, target) => {
|
|
431
433
|
let cached = dashboardOverview(jobId, target);
|
|
434
|
+
if (!cached)
|
|
435
|
+
cached = (await inventoryFromLocal(false)).find(item => item.job.id === jobId &&
|
|
436
|
+
(!target?.repository || item.job.repository === target.repository) &&
|
|
437
|
+
(!target?.prNumber || item.job.prNumber === target.prNumber));
|
|
432
438
|
if (!cached && target?.repository && target.prNumber && repositories.includes(target.repository)) {
|
|
433
439
|
cached = (await refreshPullRequest(target.repository, target.prNumber, 'control.job-lookup', { force: true }))
|
|
434
440
|
.find(item => item.job.id === jobId && item.job.repository === target.repository && item.job.prNumber === target.prNumber);
|
|
@@ -439,7 +445,7 @@ export async function runDaemon(argv) {
|
|
|
439
445
|
try {
|
|
440
446
|
const refreshed = (await refreshPullRequest(cached.job.repository, cached.job.prNumber, 'dashboard.selected-job', { force: true })).find(item => item.job.id === jobId);
|
|
441
447
|
if (!refreshed)
|
|
442
|
-
return
|
|
448
|
+
return cachedJob();
|
|
443
449
|
return { ...refreshed.job, githubState: refreshed.githubState, runtimeStatus: deriveJobRuntimeStatus(refreshed.job, refreshed.githubState), mergedAt: refreshed.mergedAt, closedAt: refreshed.closedAt, draft: refreshed.draft, publication: refreshed.publication, publicationFailure: refreshed.publicationFailure };
|
|
444
450
|
}
|
|
445
451
|
catch (error) {
|