agents-relay 1.0.165 → 1.0.166
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/cli.js
CHANGED
|
@@ -18,7 +18,7 @@ import { decisionMode, LayaCliDecisionEngine } from './decision.js';
|
|
|
18
18
|
import { discoverAgents } from './registry.js';
|
|
19
19
|
import { AgentObjectivePlanner, CommandObjectivePlanner } from './planner.js';
|
|
20
20
|
import { extendRetryBudget, transitionJobEvent, transitionTaskEvent } from './scheduler.js';
|
|
21
|
-
import {
|
|
21
|
+
import { dueBuckets, processDueScheduledJobs, syncScheduleLabels } from './scheduled-jobs.js';
|
|
22
22
|
import { resolveExecutionContext } from './execution-context.js';
|
|
23
23
|
import { defaultWorkspaceRoot, resolveManagedProjectPath, resolveRepositoryProjectPath } from './workspace.js';
|
|
24
24
|
import { runBlockerCommand } from './cli-blocker.js';
|
|
@@ -35,7 +35,6 @@ export async function ensureManagedGitHubJob(client, repositoryName, trustedAuth
|
|
|
35
35
|
const store = new GitHubStore(client, repositoryName, pr.number, trustedAuthors);
|
|
36
36
|
try {
|
|
37
37
|
const existing = await store.load(options.id);
|
|
38
|
-
await assertScheduledAutonomousRepositoryCapability(client, repositoryName, existing);
|
|
39
38
|
if (existing.schedule)
|
|
40
39
|
await syncScheduleLabels(client, repositoryName, existing);
|
|
41
40
|
return { job: existing, pr };
|
|
@@ -66,7 +65,6 @@ export async function ensureManagedGitHubJob(client, repositoryName, trustedAuth
|
|
|
66
65
|
};
|
|
67
66
|
if (options.schedule)
|
|
68
67
|
job.schedule = options.schedule;
|
|
69
|
-
await assertScheduledAutonomousRepositoryCapability(client, repositoryName, job);
|
|
70
68
|
await store.saveJob(job);
|
|
71
69
|
if (job.schedule)
|
|
72
70
|
await syncScheduleLabels(client, repositoryName, job);
|
|
@@ -450,7 +448,6 @@ async function runJobCommand(action, args) {
|
|
|
450
448
|
throw new Error('--continue-kind and --continue-target must be supplied together');
|
|
451
449
|
job.continuation = next;
|
|
452
450
|
}
|
|
453
|
-
await assertScheduledAutonomousRepositoryCapability(auth.client, repo, job);
|
|
454
451
|
job.updatedAt = new Date().toISOString();
|
|
455
452
|
await store.saveJob(job);
|
|
456
453
|
if (hasArg(args, '--schedule') && job.schedule)
|
package/dist/relayd.js
CHANGED
|
@@ -19,7 +19,9 @@ import { deriveJobRuntimeStatus } from './runtime-status.js';
|
|
|
19
19
|
import { decisionMode, LayaCliDecisionEngine } from './decision.js';
|
|
20
20
|
import { JsonlDecisionAuditStore } from './decision-audit.js';
|
|
21
21
|
import { summarizeRuntime } from './runtime-summary.js';
|
|
22
|
+
import { dueBuckets, processDueScheduledJobs } from './scheduled-jobs.js';
|
|
22
23
|
const PR_REFRESH_COALESCE_MS = 1000;
|
|
24
|
+
export const SCHEDULE_HEARTBEAT_MS = 600_000;
|
|
23
25
|
export function shouldCoalescePullRequestRefresh(completedAt, now = Date.now(), force = false) {
|
|
24
26
|
return !force && completedAt !== null && now >= completedAt && now - completedAt < PR_REFRESH_COALESCE_MS;
|
|
25
27
|
}
|
|
@@ -280,13 +282,29 @@ export async function runDaemon(argv) {
|
|
|
280
282
|
process.stderr.write(`warning: workspace reconcile unavailable: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
281
283
|
}
|
|
282
284
|
};
|
|
285
|
+
const scheduleHeartbeatSafely = async () => {
|
|
286
|
+
if (client.rateLimitStatus().limited)
|
|
287
|
+
return;
|
|
288
|
+
const now = new Date();
|
|
289
|
+
for (const repository of repositories) {
|
|
290
|
+
try {
|
|
291
|
+
await processDueScheduledJobs({ client, repository, trustedAuthors: trusted, buckets: dueBuckets(now, 36), now, engine: job => ({ reconcile: async () => { await pool.reconcileTarget(repository, job.prNumber, job.id); }, idle: async () => { } }) });
|
|
292
|
+
}
|
|
293
|
+
catch (error) {
|
|
294
|
+
process.stderr.write(`warning: scheduled heartbeat unavailable for ${repository}: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
};
|
|
283
298
|
const watchdogDefault = webhook ? SERVICE_DEFAULTS.webhookWatchdogMs : SERVICE_DEFAULTS.watchdogMs;
|
|
284
299
|
const interval = setInterval(() => void reconcileSafely(), Number(value(argv, '--interval', String(watchdogDefault))));
|
|
285
300
|
interval.unref();
|
|
286
|
-
const
|
|
301
|
+
const scheduleInterval = setInterval(() => void scheduleHeartbeatSafely(), SCHEDULE_HEARTBEAT_MS);
|
|
302
|
+
scheduleInterval.unref();
|
|
303
|
+
const stop = () => { clearInterval(interval); clearInterval(scheduleInterval); void stopWatch().finally(() => server.close(() => process.exit(0))); };
|
|
287
304
|
process.once('SIGINT', stop);
|
|
288
305
|
process.once('SIGTERM', stop);
|
|
289
306
|
console.log('Dashboard listening; repository worker pool active');
|
|
307
|
+
void scheduleHeartbeatSafely();
|
|
290
308
|
void refreshAll(true)
|
|
291
309
|
.then(items => pool.reconcileKnown(items))
|
|
292
310
|
.catch(error => process.stderr.write(`warning: startup reconciliation unavailable: ${error instanceof Error ? error.message : String(error)}\n`));
|
package/dist/scheduled-jobs.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { GitHubStore, loadManagedGitHubPullRequestGraphql } from './store.js';
|
|
2
2
|
import { bucketStart, nextScheduleRun, parseRunBucketLabel, runBucketLabel, scheduleBucket } from './scheduler.js';
|
|
3
3
|
export const AUTONOMOUS_LABEL = 'agents-relay:autonomous';
|
|
4
|
-
export const AUTONOMOUS_SCHEDULER_WORKFLOW = '.github/workflows/autonomous-scheduler.yml';
|
|
5
4
|
function searchPath(query, page) {
|
|
6
5
|
return `search/issues?q=${encodeURIComponent(query)}&sort=created&order=asc&per_page=100&page=${page}`;
|
|
7
6
|
}
|
|
@@ -18,21 +17,6 @@ export function indexedDueQuery(repository, bucket) {
|
|
|
18
17
|
const label = runBucketLabel(bucket);
|
|
19
18
|
return `repo:${repository} is:open is:pr label:"${AUTONOMOUS_LABEL}" label:"${label}"`;
|
|
20
19
|
}
|
|
21
|
-
export async function assertScheduledAutonomousRepositoryCapability(client, repository, job) {
|
|
22
|
-
if (job.executionMode !== 'autonomous' || !job.schedule)
|
|
23
|
-
return;
|
|
24
|
-
const metadata = await client.request(`repos/${repository}`);
|
|
25
|
-
const defaultBranch = typeof metadata.default_branch === 'string' && metadata.default_branch ? metadata.default_branch : 'main';
|
|
26
|
-
try {
|
|
27
|
-
await client.request(`repos/${repository}/contents/${AUTONOMOUS_SCHEDULER_WORKFLOW}?ref=${encodeURIComponent(defaultBranch)}`);
|
|
28
|
-
}
|
|
29
|
-
catch (error) {
|
|
30
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
31
|
-
if (!/404|Not Found/i.test(message))
|
|
32
|
-
throw error;
|
|
33
|
-
throw new Error(`SCHEDULED_AUTONOMOUS_UNAVAILABLE: ${repository} default branch ${defaultBranch} must contain ${AUTONOMOUS_SCHEDULER_WORKFLOW} before creating or enabling a scheduled autonomous Job`);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
20
|
async function issueLabels(client, repository, prNumber) {
|
|
37
21
|
const labels = [];
|
|
38
22
|
let page = 1;
|
package/package.json
CHANGED
|
@@ -109,9 +109,9 @@ SHA-bound final reviewer. For example, `video-director + market + style ->
|
|
|
109
109
|
review-chair -> unified revision plan` is a project pattern, not a hard-coded
|
|
110
110
|
Relay reviewer list.
|
|
111
111
|
|
|
112
|
-
A scheduled autonomous job adds cron/timezone/next-run and missed-run/concurrency policy to the trusted job marker, plus the `agents-relay:autonomous` label and one bounded time-of-day `agents-relay:next-run:*` index label. The
|
|
112
|
+
A scheduled autonomous job adds cron/timezone/next-run and missed-run/concurrency policy to the trusted job marker, plus the `agents-relay:autonomous` label and one bounded time-of-day `agents-relay:next-run:*` index label. The always-on `agents-relayd` process is the authoritative heartbeat for repositories it manages: it evaluates due schedule buckets on startup and every ten minutes and reconciles claimed work through the local worker pool. A repository may also install `.github/workflows/autonomous-scheduler.yml` as an optional cloud fallback; GitHub `schedule` delivery is best-effort and must not be relied on for precise cadence. Unscheduled autonomous Jobs and fixed Jobs do not use the scheduler heartbeat.
|
|
113
113
|
|
|
114
|
-
The
|
|
114
|
+
The scheduler heartbeat discovers only indexed due candidates, validates the marker's exact `nextRunAt`, claims the slot idempotently, advances the durable next run, and pulses `agents-relay:schedule-wake`. GitHub webhook delivery then wakes local Relay for planner/worker execution. While the schedule is active, the durable Job remains `ACTIVE`; between runs its derived runtime status is `WAITING`. Scheduled recurring Jobs do not enter one-shot final-review/auto-merge after each satisfied run; older-cycle failures are historical evidence and do not block a later slot.
|
|
115
115
|
|
|
116
116
|
|
|
117
117
|
## Task evidence and head identity
|