agents-relay 1.0.170 → 1.0.172

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.
@@ -70,14 +70,6 @@ export const dashboardHtml = `<!doctype html>
70
70
  .detail-label { margin:0; color:var(--muted); font-size:.66rem; text-transform:uppercase; letter-spacing:.04em; }
71
71
  .detail-value { margin:.12rem 0 0; font-weight:650; overflow-wrap:anywhere; }
72
72
  .section-title { margin:.8rem 0 .35rem; color:var(--muted); font-size:.72rem; letter-spacing:.05em; text-transform:uppercase; }
73
- .chat-shortcuts { margin-top:.7rem; padding-top:.65rem; border-top:1px solid var(--border); }
74
- .chat-shortcut-tabs,.chat-shortcut-list { display:flex; gap:.35rem; flex-wrap:wrap; }
75
- .chat-shortcut-tab,.chat-shortcut-button { border:1px solid var(--border); border-radius:.35rem; background:var(--surface); color:var(--text); padding:.3rem .5rem; font:inherit; font-size:.72rem; cursor:pointer; }
76
- .chat-shortcut-tab[aria-selected="true"] { border-color:var(--accent); background:var(--accent2); color:var(--accent); font-weight:750; }
77
- .chat-shortcut-button { color:var(--accent); font-weight:700; }
78
- .chat-shortcut-editor { display:grid; gap:.4rem; margin-top:.45rem; }
79
- .chat-shortcut-editor textarea { width:100%; min-height:7rem; resize:vertical; border:1px solid var(--border); border-radius:.4rem; background:var(--surface2); color:var(--text); padding:.55rem; font:12px/1.45 ui-monospace,SFMono-Regular,Menlo,monospace; }
80
- .chat-shortcut-help { margin:0; color:var(--muted); font-size:.7rem; }
81
73
  ul.tree { display:grid; gap:.25rem; min-width:0; max-width:100%; }
82
74
  ul.tree li { min-width:0; max-width:100%; }
83
75
  ul.tree ul { width:calc(100% - .6rem); min-width:0; max-width:calc(100% - .6rem); list-style:none; margin:.2rem 0 .1rem .6rem; padding-left:.6rem; border-left:1px solid var(--border); }
@@ -99,7 +91,7 @@ export const dashboardHtml = `<!doctype html>
99
91
  .event-meta { display:flex; flex-wrap:wrap; gap:.4rem; color:var(--muted); font-size:.68rem; }
100
92
  .event-kind { color:var(--accent); font-weight:800; }
101
93
  .event-message { margin:.12rem 0 0; overflow-wrap:anywhere; }
102
- #usage-view,#rules-view,#worker-rules-view { grid-column:1 / -1; }
94
+ #usage-view,#rules-view,#shortcuts-view,#worker-rules-view { grid-column:1 / -1; }
103
95
  .rules-editor { display:grid; gap:.65rem; }
104
96
  .rules-help { margin:0; color:var(--muted); font-size:.8rem; }
105
97
  .rules-textarea { width:100%; min-height:55vh; resize:vertical; border:1px solid var(--border); border-radius:.45rem; background:var(--surface2); color:var(--text); padding:.8rem; font:13px/1.55 ui-monospace,SFMono-Regular,Menlo,monospace; }
@@ -179,6 +171,7 @@ export const dashboardHtml = `<!doctype html>
179
171
  <button id="jobs-tab" type="button" role="tab" aria-selected="true" aria-controls="jobs-view">Jobs</button>
180
172
  <button id="usage-tab" type="button" role="tab" aria-selected="false" aria-controls="usage-view">Usage</button>
181
173
  <button id="rules-tab" type="button" role="tab" aria-selected="false" aria-controls="rules-view">Execution Rules</button>
174
+ <button id="shortcuts-tab" type="button" role="tab" aria-selected="false" aria-controls="shortcuts-view">Shortcuts</button>
182
175
  </nav>
183
176
  </header>
184
177
  <main>
@@ -220,6 +213,18 @@ export const dashboardHtml = `<!doctype html>
220
213
  </div>
221
214
  </div>
222
215
  </section>
216
+ <section id="shortcuts-view" role="tabpanel" aria-labelledby="shortcuts-tab" hidden>
217
+ <div class="panel">
218
+ <div class="panel-head"><h2>ChatGPT Shortcuts</h2><span class="subtle">One per line · %1 = current repo/pr</span></div>
219
+ <div class="panel-body">
220
+ <div class="rules-editor">
221
+ <p class="rules-help">Format: &lt;name&gt;: &lt;prompt&gt;. Use %1 where the current Job reference should appear.</p>
222
+ <textarea id="shortcuts-text" class="rules-textarea" spellcheck="true" aria-label="ChatGPT shortcuts"></textarea>
223
+ <div class="rules-footer"><span id="shortcuts-status" class="subtle" aria-live="polite"></span><button id="shortcuts-save" class="rules-save" type="button">Save shortcuts</button></div>
224
+ </div>
225
+ </div>
226
+ </div>
227
+ </section>
223
228
  </main>
224
229
  <script>
225
230
  'use strict';
@@ -240,9 +245,11 @@ const DASHBOARD_VERSION=${JSON.stringify(dashboardVersion)};
240
245
  const jobsTab=document.querySelector('#jobs-tab');
241
246
  const usageTab=document.querySelector('#usage-tab');
242
247
  const rulesTab=document.querySelector('#rules-tab');
248
+ const shortcutsTab=document.querySelector('#shortcuts-tab');
243
249
  const jobsView=document.querySelector('#jobs-view');
244
250
  const usageView=document.querySelector('#usage-view');
245
251
  const rulesView=document.querySelector('#rules-view');
252
+ const shortcutsView=document.querySelector('#shortcuts-view');
246
253
  const jobsWorkspace=document.querySelector('#jobs-workspace');
247
254
  const jobList=document.querySelector('#job-list');
248
255
  const jobsRefresh=document.querySelector('#jobs-refresh');
@@ -268,6 +275,9 @@ const rulesText=document.querySelector('#rules-text');
268
275
  const rulesMeta=document.querySelector('#rules-meta');
269
276
  const rulesStatus=document.querySelector('#rules-status');
270
277
  const rulesSave=document.querySelector('#rules-save');
278
+ const shortcutsText=document.querySelector('#shortcuts-text');
279
+ const shortcutsStatus=document.querySelector('#shortcuts-status');
280
+ const shortcutsSave=document.querySelector('#shortcuts-save');
271
281
  const rateLimitWarning=document.querySelector('#rate-limit-warning');
272
282
  const runtimeSummary=document.querySelector('#runtime-summary');
273
283
  let jobs=[];
@@ -339,31 +349,7 @@ function chatShortcutText(){return localStorage.getItem(CHAT_SHORTCUTS_KEY)||DEF
339
349
  function parseChatShortcuts(source){return String(source||'').split(/\\r?\\n/).map(line=>line.trim()).filter(Boolean).map(line=>{const split=line.indexOf(':');return split>0?{name:line.slice(0,split).trim(),prompt:line.slice(split+1).trim()}:null;}).filter(item=>item&&item.name&&item.prompt);}
340
350
  function jobChatRef(job){return repoName(job?.repository)+'/pr#'+value(job?.prNumber);}
341
351
  function chatShortcutUrl(prompt){return 'https://chatgpt.com/?prompt='+encodeURIComponent(prompt);}
342
- function renderChatShortcuts(job){
343
- const root=element('section','chat-shortcuts');
344
- const tabs=element('div','chat-shortcut-tabs');
345
- const shortcutsTab=element('button','chat-shortcut-tab',text('Shortcuts'));shortcutsTab.type='button';shortcutsTab.setAttribute('aria-selected','true');
346
- const editTab=element('button','chat-shortcut-tab',text('Edit'));editTab.type='button';editTab.setAttribute('aria-selected','false');
347
- tabs.append(shortcutsTab,editTab);
348
- const body=element('div');
349
- function showShortcuts(){
350
- shortcutsTab.setAttribute('aria-selected','true');editTab.setAttribute('aria-selected','false');
351
- const list=element('div','chat-shortcut-list');
352
- for(const item of parseChatShortcuts(chatShortcutText())){const button=element('button','chat-shortcut-button',text(item.name));button.type='button';button.addEventListener('click',()=>{const prompt=item.prompt.replaceAll('%1',jobChatRef(job));window.open(chatShortcutUrl(prompt),'_blank','noopener');});list.append(button);}
353
- body.replaceChildren(list.children.length?list:element('p','empty',text('No shortcuts configured.')));
354
- }
355
- function showEditor(){
356
- shortcutsTab.setAttribute('aria-selected','false');editTab.setAttribute('aria-selected','true');
357
- const editor=element('div','chat-shortcut-editor');
358
- const help=element('p','chat-shortcut-help',text('One per line: <name>: <prompt>. Use %1 for '+jobChatRef(job)+'.'));
359
- const textarea=document.createElement('textarea');textarea.value=chatShortcutText();textarea.setAttribute('aria-label','ChatGPT shortcut entries');
360
- const save=element('button','refresh-button',text('Save'));save.type='button';save.addEventListener('click',()=>{const value=textarea.value.trim();if(value)localStorage.setItem(CHAT_SHORTCUTS_KEY,value);else localStorage.removeItem(CHAT_SHORTCUTS_KEY);showShortcuts();});
361
- editor.append(help,textarea,save);body.replaceChildren(editor);
362
- }
363
- shortcutsTab.addEventListener('click',showShortcuts);editTab.addEventListener('click',showEditor);
364
- root.append(tabs,body);showShortcuts();return root;
365
- }
366
-
352
+ function chatShortcutField(job){const box=element('div','detail-group');box.append(element('p','detail-label',text('ChatGPT shortcut')));const select=document.createElement('select');select.className='job-filter job-chat-shortcut-select';select.setAttribute('aria-label','ChatGPT shortcut');select.append(new Option('',''));for(const item of parseChatShortcuts(chatShortcutText()))select.append(new Option(item.name,item.prompt));select.value='';select.addEventListener('change',()=>{if(!select.value)return;const prompt=select.value.replaceAll('%1',jobChatRef(job));window.open(chatShortcutUrl(prompt),'_blank','noopener');select.value='';});box.append(select);return box;}
367
353
  function renderJobList(){jobList.replaceChildren();const visible=filteredJobs();if(!jobs.length){jobList.append(element('li','empty',text('No durable jobs discovered.')));return;}if(!visible.length){jobList.append(element('li','empty',text('No jobs match the selected repo.')));return;}for(const job of visible){const row=element('div','job-row'+(isCompleteUnmerged(job)?' complete-unmerged':''));row.setAttribute('aria-current',String(job.id===selectedJob));row.addEventListener('click',()=>selectJob(job.id));const content=element('div','job-content');const select=document.createElement('button');select.type='button';select.className='job-button';select.setAttribute('aria-current',String(job.id===selectedJob));select.title=job.title||job.id;select.addEventListener('click',event=>{event.stopPropagation();selectJob(job.id);});select.append(element('span','job-title',text(repoName(job.repository)+'/PR#'+value(job.prNumber)+': '+value(job.title||job.id))));const states=element('span','job-status-line');states.append(element('span','badge '+value(job.state),text(value(job.state))),element('span','badge '+value(job.runtimeStatus||'IDLE'),text(value(job.runtimeStatus||'IDLE'))));if(job.executionMode==='autonomous')states.append(element('span','badge flag-autonomous',text('AUTONOMOUS')));if(job.reviewEnabled!==false&&job.githubState!=='MERGED')states.append(element('span','badge flag-review',text('REVIEW REQUIRED')));if(job.githubState==='MERGED'&&job.publication?.version)states.append(element('span','badge SUCCEEDED',text('v'+job.publication.version)));if(isCompleteUnmerged(job))states.append(element('span','badge MERGE_PENDING',text('MERGE PENDING')));const primary=element('div','job-primary');primary.append(states);const lastLine=element('div','job-last-line');lastLine.append(element('span','subtle',text(compactCounts(counts(job.tasks)))),element('span','subtle',text(relativeTime(job.updatedAt))));content.append(select,primary,lastLine);row.append(content);jobList.append(element('li','',row));}}
368
354
  function taskTagDisplay(job,task){const display=task.tag&&job?.tag?.values?.[task.tag];return typeof display==='string'&&display?display:null;}
369
355
  function taskSubtreeComplete(task,children){return ['SUCCEEDED','CANCELLED'].includes(task.state)&&(children.get(task.id)||[]).every(child=>taskSubtreeComplete(child,children));}
@@ -379,7 +365,7 @@ function taskStateField(task){const box=element('div','detail-group');box.append
379
365
  function manualOverrideField(task){const rows=Array.isArray(task.manualStateOverrides)?task.manualStateOverrides:[];const last=rows.at(-1);return field('Manual override',last?last.from+' → '+last.to+' · '+relativeTime(last.at):'—');}
380
366
  function renderTaskDetail(task){taskDetailRoot.replaceChildren();if(!task){taskDetailRoot.append(element('p','empty',text('Select a task to inspect execution details.')));return;}const routing=task.routing||{};const groups=element('div','detail-groups');groups.append(field('Task',task.id),field('Kind',task.kind||'work'),taskStateField(task),manualOverrideField(task),field('Planning decision',task.planningDecisionId||'—'),field('Planning source',task.planningSource||'—'),field('Planning round',task.planningRound===undefined?'—':String(task.planningRound)),field('Project / agent',value(task.projectName)+' / '+value(task.agentName)),field('Parent task',task.parentTaskId||'root'),field('Adapter',task.adapter),field('Provider / model',value(routing.provider)+' / '+value(routing.model)),field('Reasoning / profile',value(routing.reasoning||routing.profile)),field('Attempt',attemptBudget(task)),field('Dependencies',Array.isArray(task.dependencies)&&task.dependencies.length?task.dependencies.join(', '):'none'),taskCommentField(task),chatField(task));const job=selected();if(task.reviewHeadSha&&job)groups.append(commitField('HEAD commit',job,task.reviewHeadSha));const graph=agentGraphField(task);if(graph)groups.append(graph);if(task.plannerResult)groups.append(field('Objective status',task.plannerResult.objective_status),markdownField('Assessment',task.plannerResult.assessment));if(task.error)groups.append(field('Error',task.error));taskDetailRoot.append(groups);}
381
367
  function reviewStatus(job){const review=job?.review;if(!review)return 'Not started';if(review.outcome==='approved'&&review.headSha===job.headSha)return 'MERGE READY · approved';const stale=Array.isArray(job?.tasks)&&job.tasks.some(task=>task.agentName==='reviewer'&&task.reviewHeadSha&&job.headSha&&task.reviewHeadSha!==job.headSha);if(stale)return 'STALE · awaiting review';return String(review.outcome||'pending').replace('_',' ').toUpperCase();}
382
- function renderSelectedJob(){jobDetail.replaceChildren();const job=selected();if(!job){selectedJobTitle.textContent='';jobDetail.append(element('div','empty',text('No durable jobs discovered.')));buildTree([]);renderTaskDetail(null);return;}selectedJobTitle.textContent=value(job.title||job.id);if(job.id!==selectedJob){selectedJob=job.id;selectedTaskId=null;eventTaskId=null;clearEvents();rememberSelection();}const summary=element('div','job-summary');summary.append(field('PR state',job.githubState||'UNKNOWN'),jobStateField(job),field('Runtime status',job.runtimeStatus||'IDLE'),field('Execution mode',job.executionMode||'fixed'),field('Cadence',cadence(job)),field('Review',reviewStatus(job)),commitField('Review head SHA',job,job.review?.headSha||job.headSha||''),prField(job),field('Repository',job.repository||'—'),field('Last activity',relativeTime(job.updatedAt)),field('Task states',compactCounts(job.tasks)));jobDetail.append(summary,renderChatShortcuts(job));const selectedTask=(job.tasks||[]).find(task=>task.id===selectedTaskId)||null;if(selectedTaskId&&!selectedTask){selectedTaskId=null;eventTaskId=null;clearEvents();rememberSelection();}buildTree(job.tasks||[]);renderTaskDetail(selectedTask);renderEventScope();}
368
+ function renderSelectedJob(){jobDetail.replaceChildren();const job=selected();if(!job){selectedJobTitle.textContent='';jobDetail.append(element('div','empty',text('No durable jobs discovered.')));buildTree([]);renderTaskDetail(null);return;}selectedJobTitle.textContent=value(job.title||job.id);if(job.id!==selectedJob){selectedJob=job.id;selectedTaskId=null;eventTaskId=null;clearEvents();rememberSelection();}const summary=element('div','job-summary');summary.append(field('PR state',job.githubState||'UNKNOWN'),jobStateField(job),field('Runtime status',job.runtimeStatus||'IDLE'),field('Execution mode',job.executionMode||'fixed'),field('Cadence',cadence(job)),field('Review',reviewStatus(job)),commitField('Review head SHA',job,job.review?.headSha||job.headSha||''),prField(job),field('Repository',job.repository||'—'),field('Last activity',relativeTime(job.updatedAt)),field('Task states',compactCounts(job.tasks)),chatShortcutField(job));jobDetail.append(summary);const selectedTask=(job.tasks||[]).find(task=>task.id===selectedTaskId)||null;if(selectedTaskId&&!selectedTask){selectedTaskId=null;eventTaskId=null;clearEvents();rememberSelection();}buildTree(job.tasks||[]);renderTaskDetail(selectedTask);renderEventScope();}
383
369
  function renderAll(){jobs.sort(compareJobs);syncFilters();renderJobList();renderSelectedJob();syncMobileJobsView();}
384
370
  function isCompleteUnmerged(job){return job.runtimeStatus==='MERGE_PENDING';}
385
371
  function isTerminalJob(job){return ['COMPLETED','CANCELLED'].includes(job.state)||['MERGED','CLOSED'].includes(job.githubState);}
@@ -431,7 +417,9 @@ function githubUsageCard(snapshot,status){
431
417
  async function drawUsage(){try{const overview=await loadJSON('/api/usage');usageRoot.replaceChildren();usageMeta.textContent='Real account telemetry when available';const providers=Array.isArray(overview.providers)?overview.providers:[];const credits=Array.isArray(overview.credits)?overview.credits:[];const grid=element('div','usage-providers');if(overview.github||overview.githubStatus)grid.append(githubUsageCard(overview.github,overview.githubStatus));for(const provider of providers)grid.append(usageProvider(provider));for(const provider of credits)grid.append(creditProvider(provider));if(!grid.children.length){usageRoot.append(element('p','empty',text('Usage telemetry is unavailable.')));return;}usageRoot.append(grid);}catch(error){usageMeta.textContent='Unavailable';usageRoot.replaceChildren(element('p','error-state',text('Usage unavailable: '+error.message)));}}
432
418
  async function drawRules(){rulesStatus.textContent='';try{const rules=await loadJSON('/api/execution-router-rules');rulesText.value=typeof rules.text==='string'?rules.text:'';rulesMeta.textContent=rules.hash&&rules.hash!=='none'?'Loaded · '+rules.hash.slice(0,12):'No custom rules saved';}catch(error){rulesMeta.textContent='Unavailable';rulesStatus.textContent='Could not load rules: '+error.message;}}
433
419
  async function saveRules(){rulesSave.disabled=true;rulesStatus.textContent='Saving…';try{const response=await fetch('/api/execution-router-rules',{method:'PUT',headers:{'content-type':'text/plain; charset=utf-8'},body:rulesText.value});if(!response.ok)throw new Error(response.status+' '+await response.text());const rules=await response.json();rulesMeta.textContent='Saved · '+rules.hash.slice(0,12);rulesStatus.textContent='Saved. New routing decisions will load these rules.';}catch(error){rulesStatus.textContent='Save failed: '+error.message;}finally{rulesSave.disabled=false;}}
434
- function showView(view){const isJobs=view==='jobs';const isUsage=view==='usage';const isRules=view==='rules';jobsView.hidden=!isJobs;jobsWorkspace.hidden=!isJobs;usageView.hidden=!isUsage;rulesView.hidden=!isRules;jobsTab.setAttribute('aria-selected',String(isJobs));usageTab.setAttribute('aria-selected',String(isUsage));rulesTab.setAttribute('aria-selected',String(isRules));if(isJobs)void refresh();else if(isUsage)void drawUsage();else void drawRules();}
420
+ async function drawShortcuts(){shortcutsStatus.textContent='';shortcutsText.value=chatShortcutText();}
421
+ 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.';}
422
+ function showView(view){const isJobs=view==='jobs';const isUsage=view==='usage';const isRules=view==='rules';const isShortcuts=view==='shortcuts';jobsView.hidden=!isJobs;jobsWorkspace.hidden=!isJobs;usageView.hidden=!isUsage;rulesView.hidden=!isRules;shortcutsView.hidden=!isShortcuts;jobsTab.setAttribute('aria-selected',String(isJobs));usageTab.setAttribute('aria-selected',String(isUsage));rulesTab.setAttribute('aria-selected',String(isRules));shortcutsTab.setAttribute('aria-selected',String(isShortcuts));if(isJobs)void refresh();else if(isUsage)void drawUsage();else if(isRules)void drawRules();else void drawShortcuts();}
435
423
  jobsTab.addEventListener('click',()=>{location.hash=selectedJob?'#/jobs/'+encodeURIComponent(selectedJob):'#/jobs';showView('jobs');});
436
424
  jobsRefresh.addEventListener('click',()=>void refresh(true));
437
425
  eventsAllTasks.addEventListener('click',()=>{eventTaskId=null;clearEvents();renderEventScope();void loadEventHistory();});
@@ -446,7 +434,9 @@ mobileJobs.addEventListener('change',syncMobileJobsView);
446
434
  window.addEventListener('hashchange',syncMobileJobsView);
447
435
  usageTab.addEventListener('click',()=>{location.hash='#/usage';showView('usage');});
448
436
  rulesTab.addEventListener('click',()=>{location.hash='#/execution-router-rules';showView('rules');});
437
+ shortcutsTab.addEventListener('click',()=>{location.hash='#/shortcuts';showView('shortcuts');});
449
438
  rulesSave.addEventListener('click',()=>void saveRules());
439
+ shortcutsSave.addEventListener('click',saveShortcuts);
450
440
  window.addEventListener('keydown',event=>{if(event.key==='Tab'&&event.altKey){event.preventDefault();const next=location.hash==='#/usage'?'rules':location.hash==='#/execution-router-rules'?'jobs':'usage';location.hash=next==='usage'?'#/usage':next==='rules'?'#/execution-router-rules':(selectedJob?'#/jobs/'+encodeURIComponent(selectedJob):'#/jobs');showView(next);}});
451
441
  function parseMessage(message){try{return JSON.parse(message.data);}catch{return null;}}
452
442
  const stream=new EventSource('/events');
@@ -454,8 +444,8 @@ stream.onopen=()=>{connection.textContent='';connection.setAttribute('aria-label
454
444
  stream.onerror=()=>{connection.textContent='';connection.setAttribute('aria-label','Event stream reconnecting');connection.className='status-pill warn';};
455
445
  stream.onmessage=message=>{const payload=parseMessage(message);if(payload?.job){const index=jobs.findIndex(job=>job.id===payload.job.id);if(index>=0)jobs[index]=payload.job;else jobs.push(payload.job);renderAll();void drawRuntimeSummary();}};
456
446
  stream.addEventListener('relay',message=>{const payload=parseMessage(message);if(payload?.job){const index=jobs.findIndex(job=>job.id===payload.job.id);if(index>=0)jobs[index]=payload.job;else jobs.push(payload.job);}if(payload?.event)addEvent(payload.event);renderAll();void drawRuntimeSummary();});
457
- if(location.hash==='#/usage')showView('usage');else if(location.hash==='#/execution-router-rules')showView('rules');else showView('jobs');
458
- setInterval(()=>{if(location.hash!=='#/usage'&&location.hash!=='#/execution-router-rules')void refresh();},300000);
447
+ if(location.hash==='#/usage')showView('usage');else if(location.hash==='#/execution-router-rules')showView('rules');else if(location.hash==='#/shortcuts')showView('shortcuts');else showView('jobs');
448
+ setInterval(()=>{if(location.hash!=='#/usage'&&location.hash!=='#/execution-router-rules'&&location.hash!=='#/shortcuts')void refresh();},300000);
459
449
  setInterval(()=>{if(location.hash==='#/usage')void drawUsage();},30000);
460
450
  setInterval(()=>void refreshVersion(),30000);
461
451
  async function refreshSelected(){if(!selectedJob){await refresh();return;}selectedJobRefresh.disabled=true;selectedJobRefresh.textContent='Refreshing…';try{const updated=await loadJSON('/api/job?id='+encodeURIComponent(selectedJob));const index=jobs.findIndex(job=>job.id===selectedJob);if(index>=0)jobs[index]=updated;else jobs.push(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;selectedJobRefresh.textContent='Refresh';}}
@@ -104,7 +104,7 @@ export class ReconcilerReviewBase extends ReconcilerRuntimeBase {
104
104
  const registered = await this.store.listAgents();
105
105
  const candidates = workerCandidates(this.options.adapters, registered, usage);
106
106
  const runtimeSummary = this.options.runtimeSummaryProvider?.();
107
- for (const task of job.tasks.filter(item => item.kind !== 'planner' && item.agentName !== 'reviewer' && item.adapter !== 'orchestrator' && ['QUEUED', 'BLOCKED'].includes(item.state) && item.output && !item.workerSelection && (item.workerRequirements || item.planningSource === 'model-planner'))) {
107
+ for (const task of job.tasks.filter(item => item.kind !== 'planner' && item.adapter !== 'orchestrator' && ['QUEUED', 'BLOCKED'].includes(item.state) && item.output && !item.workerSelection && (item.workerRequirements || item.planningSource === 'model-planner'))) {
108
108
  const requirements = task.workerRequirements ?? { capabilities: [] };
109
109
  const taskFacts = {
110
110
  id: task.id,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agents-relay",
3
- "version": "1.0.170",
3
+ "version": "1.0.172",
4
4
  "description": "Durable async agent jobs coordinated through GitHub pull requests",
5
5
  "type": "module",
6
6
  "bin": {