agents-relay 1.0.152 → 1.0.153
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 -34
- package/dist/dashboard.js +5 -46
- package/dist/execution-routing.js +5 -8
- package/dist/model-routing.js +3 -3
- package/dist/routing-policy.js +7 -16
- package/dist/worker-routing.js +2 -2
- package/package.json +1 -1
package/dist/dashboard-html.js
CHANGED
|
@@ -170,8 +170,7 @@ export const dashboardHtml = `<!doctype html>
|
|
|
170
170
|
<nav role="tablist" aria-label="Console views">
|
|
171
171
|
<button id="jobs-tab" type="button" role="tab" aria-selected="true" aria-controls="jobs-view">Jobs</button>
|
|
172
172
|
<button id="usage-tab" type="button" role="tab" aria-selected="false" aria-controls="usage-view">Usage</button>
|
|
173
|
-
<button id="rules-tab" type="button" role="tab" aria-selected="false" aria-controls="rules-view">
|
|
174
|
-
<button id="worker-rules-tab" type="button" role="tab" aria-selected="false" aria-controls="worker-rules-view">Worker Rules</button>
|
|
173
|
+
<button id="rules-tab" type="button" role="tab" aria-selected="false" aria-controls="rules-view">Execution Rules</button>
|
|
175
174
|
</nav>
|
|
176
175
|
</header>
|
|
177
176
|
<main>
|
|
@@ -203,28 +202,16 @@ export const dashboardHtml = `<!doctype html>
|
|
|
203
202
|
</section>
|
|
204
203
|
<section id="rules-view" role="tabpanel" aria-labelledby="rules-tab" hidden>
|
|
205
204
|
<div class="panel">
|
|
206
|
-
<div class="panel-head"><h2>
|
|
205
|
+
<div class="panel-head"><h2>Execution Router Rules</h2><span id="rules-meta" class="subtle"></span></div>
|
|
207
206
|
<div class="panel-body">
|
|
208
207
|
<div class="rules-editor">
|
|
209
|
-
<p class="rules-help">Free-text
|
|
210
|
-
<textarea id="rules-text" class="rules-textarea" spellcheck="true" aria-label="
|
|
208
|
+
<p class="rules-help">Free-text soft policy loaded by the Execution Router for every worker+model routing decision. Impossible routes remain excluded by code; Codex remains the deterministic fallback if decision fails.</p>
|
|
209
|
+
<textarea id="rules-text" class="rules-textarea" spellcheck="true" aria-label="Execution router rules" placeholder="Example: Prefer ZAI glm-5.3-flash high while quota is available. Use ChatGPT for research when appropriate. Codex is fallback only."></textarea>
|
|
211
210
|
<div class="rules-footer"><span id="rules-status" class="subtle" aria-live="polite"></span><button id="rules-save" class="rules-save" type="button">Save rules</button></div>
|
|
212
211
|
</div>
|
|
213
212
|
</div>
|
|
214
213
|
</div>
|
|
215
214
|
</section>
|
|
216
|
-
<section id="worker-rules-view" role="tabpanel" aria-labelledby="worker-rules-tab" hidden>
|
|
217
|
-
<div class="panel">
|
|
218
|
-
<div class="panel-head"><h2>Worker Router Rules</h2><span id="worker-rules-meta" class="subtle"></span></div>
|
|
219
|
-
<div class="panel-body">
|
|
220
|
-
<div class="rules-editor">
|
|
221
|
-
<p class="rules-help">Free-text guidance loaded by the worker decider when choosing ChatGPT versus Codex. Use this for local preferences; worker legality and Model Router remain separate.</p>
|
|
222
|
-
<textarea id="worker-rules-text" class="rules-textarea" spellcheck="true" aria-label="Worker router rules" placeholder="Example: Prefer ChatGPT unless the task requires native local execution or computer-use that ChatGPT plus MacBridge cannot perform."></textarea>
|
|
223
|
-
<div class="rules-footer"><span id="worker-rules-status" class="subtle" aria-live="polite"></span><button id="worker-rules-save" class="rules-save" type="button">Save rules</button></div>
|
|
224
|
-
</div>
|
|
225
|
-
</div>
|
|
226
|
-
</div>
|
|
227
|
-
</section>
|
|
228
215
|
</main>
|
|
229
216
|
<script>
|
|
230
217
|
'use strict';
|
|
@@ -245,11 +232,9 @@ const DASHBOARD_VERSION=${JSON.stringify(dashboardVersion)};
|
|
|
245
232
|
const jobsTab=document.querySelector('#jobs-tab');
|
|
246
233
|
const usageTab=document.querySelector('#usage-tab');
|
|
247
234
|
const rulesTab=document.querySelector('#rules-tab');
|
|
248
|
-
const workerRulesTab=document.querySelector('#worker-rules-tab');
|
|
249
235
|
const jobsView=document.querySelector('#jobs-view');
|
|
250
236
|
const usageView=document.querySelector('#usage-view');
|
|
251
237
|
const rulesView=document.querySelector('#rules-view');
|
|
252
|
-
const workerRulesView=document.querySelector('#worker-rules-view');
|
|
253
238
|
const jobsWorkspace=document.querySelector('#jobs-workspace');
|
|
254
239
|
const jobList=document.querySelector('#job-list');
|
|
255
240
|
const jobsRefresh=document.querySelector('#jobs-refresh');
|
|
@@ -275,10 +260,6 @@ const rulesText=document.querySelector('#rules-text');
|
|
|
275
260
|
const rulesMeta=document.querySelector('#rules-meta');
|
|
276
261
|
const rulesStatus=document.querySelector('#rules-status');
|
|
277
262
|
const rulesSave=document.querySelector('#rules-save');
|
|
278
|
-
const workerRulesText=document.querySelector('#worker-rules-text');
|
|
279
|
-
const workerRulesMeta=document.querySelector('#worker-rules-meta');
|
|
280
|
-
const workerRulesStatus=document.querySelector('#worker-rules-status');
|
|
281
|
-
const workerRulesSave=document.querySelector('#worker-rules-save');
|
|
282
263
|
const rateLimitWarning=document.querySelector('#rate-limit-warning');
|
|
283
264
|
const runtimeSummary=document.querySelector('#runtime-summary');
|
|
284
265
|
let jobs=[];
|
|
@@ -397,11 +378,9 @@ function githubUsageCard(snapshot,status){
|
|
|
397
378
|
return card;
|
|
398
379
|
}
|
|
399
380
|
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)));}}
|
|
400
|
-
async function drawRules(){rulesStatus.textContent='';try{const rules=await loadJSON('/api/
|
|
401
|
-
async function saveRules(){rulesSave.disabled=true;rulesStatus.textContent='Saving…';try{const response=await fetch('/api/
|
|
402
|
-
|
|
403
|
-
async function saveWorkerRules(){workerRulesSave.disabled=true;workerRulesStatus.textContent='Saving…';try{const response=await fetch('/api/worker-router-rules',{method:'PUT',headers:{'content-type':'text/plain; charset=utf-8'},body:workerRulesText.value});if(!response.ok)throw new Error(response.status+' '+await response.text());const rules=await response.json();workerRulesMeta.textContent='Saved · '+rules.hash.slice(0,12);workerRulesStatus.textContent='Saved. New worker decisions will load these rules.';}catch(error){workerRulesStatus.textContent='Save failed: '+error.message;}finally{workerRulesSave.disabled=false;}}
|
|
404
|
-
function showView(view){const isJobs=view==='jobs';const isUsage=view==='usage';const isRules=view==='rules';const isWorkerRules=view==='worker-rules';jobsView.hidden=!isJobs;jobsWorkspace.hidden=!isJobs;usageView.hidden=!isUsage;rulesView.hidden=!isRules;workerRulesView.hidden=!isWorkerRules;jobsTab.setAttribute('aria-selected',String(isJobs));usageTab.setAttribute('aria-selected',String(isUsage));rulesTab.setAttribute('aria-selected',String(isRules));workerRulesTab.setAttribute('aria-selected',String(isWorkerRules));if(isJobs)void refresh();else if(isUsage)void drawUsage();else if(isRules)void drawRules();else void drawWorkerRules();}
|
|
381
|
+
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;}}
|
|
382
|
+
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;}}
|
|
383
|
+
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();}
|
|
405
384
|
jobsTab.addEventListener('click',()=>{location.hash=selectedJob?'#/jobs/'+encodeURIComponent(selectedJob):'#/jobs';showView('jobs');});
|
|
406
385
|
jobsRefresh.addEventListener('click',()=>void refresh(true));
|
|
407
386
|
eventsAllTasks.addEventListener('click',()=>{eventTaskId=null;clearEvents();renderEventScope();void loadEventHistory();});
|
|
@@ -415,19 +394,17 @@ mobileJobsBack.addEventListener('click',()=>{location.hash='#/jobs';syncMobileJo
|
|
|
415
394
|
mobileJobs.addEventListener('change',syncMobileJobsView);
|
|
416
395
|
window.addEventListener('hashchange',syncMobileJobsView);
|
|
417
396
|
usageTab.addEventListener('click',()=>{location.hash='#/usage';showView('usage');});
|
|
418
|
-
rulesTab.addEventListener('click',()=>{location.hash='#/
|
|
419
|
-
workerRulesTab.addEventListener('click',()=>{location.hash='#/worker-router-rules';showView('worker-rules');});
|
|
397
|
+
rulesTab.addEventListener('click',()=>{location.hash='#/execution-router-rules';showView('rules');});
|
|
420
398
|
rulesSave.addEventListener('click',()=>void saveRules());
|
|
421
|
-
|
|
422
|
-
window.addEventListener('keydown',event=>{if(event.key==='Tab'&&event.altKey){event.preventDefault();const next=location.hash==='#/usage'?'rules':location.hash==='#/model-router-rules'?'worker-rules':location.hash==='#/worker-router-rules'?'jobs':'usage';location.hash=next==='usage'?'#/usage':next==='rules'?'#/model-router-rules':next==='worker-rules'?'#/worker-router-rules':(selectedJob?'#/jobs/'+encodeURIComponent(selectedJob):'#/jobs');showView(next);}});
|
|
399
|
+
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);}});
|
|
423
400
|
function parseMessage(message){try{return JSON.parse(message.data);}catch{return null;}}
|
|
424
401
|
const stream=new EventSource('/events');
|
|
425
402
|
stream.onopen=()=>{connection.textContent='';connection.setAttribute('aria-label','Event stream connected');connection.className='status-pill ok';};
|
|
426
403
|
stream.onerror=()=>{connection.textContent='';connection.setAttribute('aria-label','Event stream reconnecting');connection.className='status-pill warn';};
|
|
427
404
|
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();}};
|
|
428
405
|
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();});
|
|
429
|
-
if(location.hash==='#/usage')showView('usage');else if(location.hash==='#/
|
|
430
|
-
setInterval(()=>{if(location.hash!=='#/usage'&&location.hash!=='#/
|
|
406
|
+
if(location.hash==='#/usage')showView('usage');else if(location.hash==='#/execution-router-rules')showView('rules');else showView('jobs');
|
|
407
|
+
setInterval(()=>{if(location.hash!=='#/usage'&&location.hash!=='#/execution-router-rules')void refresh();},300000);
|
|
431
408
|
setInterval(()=>{if(location.hash==='#/usage')void drawUsage();},30000);
|
|
432
409
|
setInterval(()=>void refreshVersion(),30000);
|
|
433
410
|
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';}}
|
package/dist/dashboard.js
CHANGED
|
@@ -5,7 +5,7 @@ import { join } from 'node:path';
|
|
|
5
5
|
import { readUsageSnapshot } from './usage.js';
|
|
6
6
|
import { deriveJobRuntimeStatus } from './runtime-status.js';
|
|
7
7
|
import { DeliveryDeduper, parseGitHubWebhook, verifyGitHubWebhook } from './github-webhook.js';
|
|
8
|
-
import {
|
|
8
|
+
import { loadExecutionRouterRules, saveExecutionRouterRules } from './routing-policy.js';
|
|
9
9
|
import { summarizeRuntime } from './runtime-summary.js';
|
|
10
10
|
const GITHUB_REPOSITORY_PATTERN = /^([A-Za-z0-9](?:[A-Za-z0-9-]{0,36}[A-Za-z0-9])?)\/([A-Za-z0-9._-]{1,100})$/;
|
|
11
11
|
const SSE_HEARTBEAT_MS = 15000;
|
|
@@ -327,10 +327,10 @@ export function serveDashboard(store, port = 8787, bus, refreshMs = 300000, jobI
|
|
|
327
327
|
res.end(JSON.stringify(events));
|
|
328
328
|
return;
|
|
329
329
|
}
|
|
330
|
-
if (req.url === '/api/
|
|
330
|
+
if (req.url === '/api/execution-router-rules') {
|
|
331
331
|
if (req.method === 'GET') {
|
|
332
332
|
try {
|
|
333
|
-
const rules = await
|
|
333
|
+
const rules = await loadExecutionRouterRules();
|
|
334
334
|
res.writeHead(200, { 'content-type': 'application/json' });
|
|
335
335
|
res.end(JSON.stringify(rules));
|
|
336
336
|
}
|
|
@@ -348,54 +348,13 @@ export function serveDashboard(store, port = 8787, bus, refreshMs = 300000, jobI
|
|
|
348
348
|
size += buffer.length;
|
|
349
349
|
if (size > 64 * 1024) {
|
|
350
350
|
res.writeHead(413);
|
|
351
|
-
res.end('
|
|
351
|
+
res.end('Execution router rules exceed 64 KiB');
|
|
352
352
|
return;
|
|
353
353
|
}
|
|
354
354
|
chunks.push(buffer);
|
|
355
355
|
}
|
|
356
356
|
try {
|
|
357
|
-
const rules = await
|
|
358
|
-
res.writeHead(200, { 'content-type': 'application/json' });
|
|
359
|
-
res.end(JSON.stringify(rules));
|
|
360
|
-
}
|
|
361
|
-
catch (error) {
|
|
362
|
-
res.writeHead(500);
|
|
363
|
-
res.end(error instanceof Error ? error.message : String(error));
|
|
364
|
-
}
|
|
365
|
-
return;
|
|
366
|
-
}
|
|
367
|
-
res.writeHead(405, { allow: 'GET, PUT' });
|
|
368
|
-
res.end('Method not allowed');
|
|
369
|
-
return;
|
|
370
|
-
}
|
|
371
|
-
if (req.url === '/api/worker-router-rules') {
|
|
372
|
-
if (req.method === 'GET') {
|
|
373
|
-
try {
|
|
374
|
-
const rules = await loadWorkerRouterRules();
|
|
375
|
-
res.writeHead(200, { 'content-type': 'application/json' });
|
|
376
|
-
res.end(JSON.stringify(rules));
|
|
377
|
-
}
|
|
378
|
-
catch (error) {
|
|
379
|
-
res.writeHead(500);
|
|
380
|
-
res.end(error instanceof Error ? error.message : String(error));
|
|
381
|
-
}
|
|
382
|
-
return;
|
|
383
|
-
}
|
|
384
|
-
if (req.method === 'PUT') {
|
|
385
|
-
const chunks = [];
|
|
386
|
-
let size = 0;
|
|
387
|
-
for await (const chunk of req) {
|
|
388
|
-
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
389
|
-
size += buffer.length;
|
|
390
|
-
if (size > 64 * 1024) {
|
|
391
|
-
res.writeHead(413);
|
|
392
|
-
res.end('Worker router rules exceed 64 KiB');
|
|
393
|
-
return;
|
|
394
|
-
}
|
|
395
|
-
chunks.push(buffer);
|
|
396
|
-
}
|
|
397
|
-
try {
|
|
398
|
-
const rules = await saveWorkerRouterRules(Buffer.concat(chunks).toString('utf8'));
|
|
357
|
+
const rules = await saveExecutionRouterRules(Buffer.concat(chunks).toString('utf8'));
|
|
399
358
|
res.writeHead(200, { 'content-type': 'application/json' });
|
|
400
359
|
res.end(JSON.stringify(rules));
|
|
401
360
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { chooseWithDecision } from './decision.js';
|
|
2
2
|
import { makeDecisionAudit } from './decision-audit.js';
|
|
3
3
|
import { discoverModelCandidates } from './model-routing.js';
|
|
4
|
-
import {
|
|
4
|
+
import { loadExecutionRouterRules, loadRoutingPolicy, policyAllows } from './routing-policy.js';
|
|
5
5
|
import { DEFAULT_CHATGPT_PREFERRED_CONCURRENCY, eligibleWorkerCandidates } from './worker-routing.js';
|
|
6
6
|
function sameModelRoute(candidate, route) {
|
|
7
7
|
return (!route.provider || candidate.provider === route.provider)
|
|
@@ -53,10 +53,10 @@ function workerSelection(worker, selectedBy, evidence) {
|
|
|
53
53
|
export async function discoverExecutionRoutes(workers, requirements, options = {}) {
|
|
54
54
|
const legalWorkers = eligibleWorkerCandidates(workers, requirements);
|
|
55
55
|
const registered = options.registered ?? [];
|
|
56
|
-
const policy = await loadRoutingPolicy(options.policyPath, options.
|
|
56
|
+
const policy = await loadRoutingPolicy(options.policyPath, options.rulesPath);
|
|
57
57
|
const routes = [];
|
|
58
58
|
for (const worker of legalWorkers) {
|
|
59
|
-
const models = await discoverModelCandidates(worker, registered, options.usage, undefined, options.
|
|
59
|
+
const models = await discoverModelCandidates(worker, registered, options.usage, undefined, options.rulesPath);
|
|
60
60
|
for (const model of models) {
|
|
61
61
|
if (!modelUsable(model, options.routingConstraints, options.excludedRoutes, policy))
|
|
62
62
|
continue;
|
|
@@ -70,11 +70,8 @@ export async function routeExecution(workers, requirements, options = {}) {
|
|
|
70
70
|
if (routes.length === 0)
|
|
71
71
|
throw new Error('No executable worker+model route satisfies authoritative constraints');
|
|
72
72
|
const fallback = fallbackRoute(routes);
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
loadModelRouterRules(options.modelRulesPath),
|
|
76
|
-
]);
|
|
77
|
-
const environmentRules = [workerRules.text, modelRules.text].filter(Boolean).join('\n');
|
|
73
|
+
const rules = await loadExecutionRouterRules(options.rulesPath);
|
|
74
|
+
const environmentRules = rules.text;
|
|
78
75
|
const preferredChatgptConcurrency = options.preferredChatgptConcurrency ?? DEFAULT_CHATGPT_PREFERRED_CONCURRENCY;
|
|
79
76
|
const chatgptRunning = options.chatgptRunning ?? 0;
|
|
80
77
|
const facts = JSON.parse(JSON.stringify({
|
package/dist/model-routing.js
CHANGED
|
@@ -4,7 +4,7 @@ import { readdir, readFile } from 'node:fs/promises';
|
|
|
4
4
|
import { chooseWithDecision } from './decision.js';
|
|
5
5
|
import { usageForProvider } from './worker-routing.js';
|
|
6
6
|
import { makeDecisionAudit } from './decision-audit.js';
|
|
7
|
-
import {
|
|
7
|
+
import { loadExecutionRouterRules, loadRoutingPolicy, policyAllows, policyRank } from './routing-policy.js';
|
|
8
8
|
function topLevelCodexConfig(text) {
|
|
9
9
|
const result = {};
|
|
10
10
|
let inSection = false;
|
|
@@ -63,7 +63,7 @@ function withResource(candidate, usage) {
|
|
|
63
63
|
export async function discoverModelCandidates(worker, registered, usage, codexHome = process.env.CODEX_HOME ?? join(homedir(), '.codex'), rulesPath) {
|
|
64
64
|
const registration = registered.find(item => item.id === worker.workerId);
|
|
65
65
|
if (registration?.runtime.provider && registration.runtime.model) {
|
|
66
|
-
const rules = await
|
|
66
|
+
const rules = await loadExecutionRouterRules(rulesPath);
|
|
67
67
|
if (!nonOpenAiModelAuthorized(registration.runtime.provider, registration.runtime.model, rules.text))
|
|
68
68
|
return [];
|
|
69
69
|
return [withResource({
|
|
@@ -76,7 +76,7 @@ export async function discoverModelCandidates(worker, registered, usage, codexHo
|
|
|
76
76
|
return [withResource({ id: 'chatgpt/account-defaults', provider: 'chatgpt', model: 'account-defaults' }, usage)];
|
|
77
77
|
}
|
|
78
78
|
const base = await readConfig(join(codexHome, 'config.toml')) ?? {};
|
|
79
|
-
const rules = await
|
|
79
|
+
const rules = await loadExecutionRouterRules(rulesPath);
|
|
80
80
|
const candidates = [];
|
|
81
81
|
if (base.model) {
|
|
82
82
|
const provider = base.provider ?? 'openai';
|
package/dist/routing-policy.js
CHANGED
|
@@ -44,27 +44,18 @@ async function saveRouterRules(path, text) {
|
|
|
44
44
|
await writeFile(path, text, 'utf8');
|
|
45
45
|
return { path, hash: createHash('sha256').update(text).digest('hex'), text };
|
|
46
46
|
}
|
|
47
|
-
export function
|
|
48
|
-
return explicit ?? process.env.
|
|
47
|
+
export function executionRouterRulesPath(explicit) {
|
|
48
|
+
return explicit ?? process.env.AGENTS_RELAY_EXECUTION_ROUTER_RULES ?? join(process.cwd(), '.agents-relay-execution-router-rules.md');
|
|
49
49
|
}
|
|
50
|
-
export function
|
|
51
|
-
return
|
|
50
|
+
export async function loadExecutionRouterRules(explicit) {
|
|
51
|
+
return await loadRouterRules(executionRouterRulesPath(explicit));
|
|
52
52
|
}
|
|
53
|
-
export async function
|
|
54
|
-
return await
|
|
55
|
-
}
|
|
56
|
-
export async function saveModelRouterRules(text, explicit) {
|
|
57
|
-
return await saveRouterRules(modelRouterRulesPath(explicit), text);
|
|
58
|
-
}
|
|
59
|
-
export async function loadWorkerRouterRules(explicit) {
|
|
60
|
-
return await loadRouterRules(workerRouterRulesPath(explicit));
|
|
61
|
-
}
|
|
62
|
-
export async function saveWorkerRouterRules(text, explicit) {
|
|
63
|
-
return await saveRouterRules(workerRouterRulesPath(explicit), text);
|
|
53
|
+
export async function saveExecutionRouterRules(text, explicit) {
|
|
54
|
+
return await saveRouterRules(executionRouterRulesPath(explicit), text);
|
|
64
55
|
}
|
|
65
56
|
export async function loadRoutingPolicy(explicit, explicitRules) {
|
|
66
57
|
const path = policyPath(explicit);
|
|
67
|
-
const rules = await
|
|
58
|
+
const rules = await loadExecutionRouterRules(explicitRules);
|
|
68
59
|
try {
|
|
69
60
|
const text = await readFile(path, 'utf8');
|
|
70
61
|
const policy = safePolicy(JSON.parse(text));
|
package/dist/worker-routing.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { chooseWithDecision } from './decision.js';
|
|
2
|
-
import {
|
|
2
|
+
import { loadExecutionRouterRules } from './routing-policy.js';
|
|
3
3
|
export const DEFAULT_CHATGPT_PREFERRED_CONCURRENCY = 10;
|
|
4
4
|
export const DEFAULT_WORKER_ROUTER_PRINCIPLES = [
|
|
5
5
|
'User-provided environment_rules are higher-priority routing guidance than these built-in defaults. Follow user guidance when it conflicts with a default unless doing so would violate an authoritative hard constraint.',
|
|
@@ -137,7 +137,7 @@ export async function routeWorker(candidates, requirements, options = {}) {
|
|
|
137
137
|
const evidence = { mode: options.decisionMode ?? 'off', fallback: true };
|
|
138
138
|
return { candidate: deterministic, selection: selection(deterministic, 'worker-router:constraint', evidence) };
|
|
139
139
|
}
|
|
140
|
-
const rules = await
|
|
140
|
+
const rules = await loadExecutionRouterRules(options.rulesPath);
|
|
141
141
|
const facts = JSON.parse(JSON.stringify({
|
|
142
142
|
task: options.taskFacts ?? {},
|
|
143
143
|
explicit_constraints: { runtime: requirements.runtime ?? null },
|