agents-relay 2.0.69 → 2.0.70

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.
@@ -1,9 +1,7 @@
1
1
  import { openLocalManagedStore } from './local-managed-store.js';
2
2
  import { createJobEntity } from './workflow/entity-creation.js';
3
3
  import { normalizeThreadId } from './reviewer-thread.js';
4
- import { discoverLocalRelayStoreSnapshots } from './relay-store.js';
5
- import { homedir } from 'node:os';
6
- import { join } from 'node:path';
4
+ import { discoverLocalRelayStoreSnapshots, relayStoreDirectory } from './relay-store.js';
7
5
  /**
8
6
  * Job lifecycle bootstrap: SQLite is authoritative. No GitHub request is
9
7
  * required; an explicit PR number is optional external metadata.
@@ -16,7 +14,7 @@ export async function ensureManagedGitHubJob(_client, repositoryName, _trustedAu
16
14
  throw new Error('Invalid external PR binding number');
17
15
  const placeholder = { number: requestedNumber, title: options.title ?? options.id,
18
16
  body: options.body ?? options.objective ?? '', state: 'OPEN', head: options.head ?? '', base: options.base ?? '' };
19
- const directory = options.storeDirectory ?? join(homedir(), '.agents-relay', 'stores');
17
+ const directory = options.storeDirectory ?? relayStoreDirectory();
20
18
  const alreadyKnown = (await discoverLocalRelayStoreSnapshots(directory)).flatMap(snapshot => snapshot.jobs.filter(job => job.id === options.id).map(job => ({ job, path: snapshot.path })));
21
19
  if (alreadyKnown.length > 1)
22
20
  throw new Error('Duplicate immutable Job ID across SQLite stores: ' + options.id);
@@ -4,8 +4,7 @@ import { githubAuthContext } from './github-auth.js';
4
4
  import { noGitHubMode } from './no-github.js';
5
5
  import { basename } from 'node:path';
6
6
  import { createJobIdRegistryResolver } from './local-job-registry.js';
7
- import { homedir } from 'node:os';
8
- import { join } from 'node:path';
7
+ import { relayStoreDirectory } from './relay-store.js';
9
8
  import { NatsEventBus, eventFor } from './events.js';
10
9
  import { parseJobSchedule } from './scheduler.js';
11
10
  import { parseJobTagJson } from './task-tag.js';
@@ -73,7 +72,7 @@ export async function storeFor(args) {
73
72
  const jobId = arg(args, '--id');
74
73
  if (!repo || !jobId)
75
74
  throw new Error('Local Job requires --repo and --id (or explicit --file)');
76
- const directory = arg(args, '--store-dir') || join(homedir(), '.agents-relay', 'stores');
75
+ const directory = arg(args, '--store-dir') || relayStoreDirectory();
77
76
  const binding = await createJobIdRegistryResolver(directory, new Map())(jobId);
78
77
  if (!binding || binding.job.repository !== repo)
79
78
  throw new Error('Immutable local Job not found for repository: ' + jobId);
package/dist/cli.js CHANGED
@@ -1,9 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { homedir } from 'node:os';
3
- import { join } from 'node:path';
4
2
  import { configureLocalGitMerge } from './cli-local-merge-config.js';
5
3
  import { noGitHubMode } from './no-github.js';
6
- import { discoverLocalRelayStoreSnapshots } from './relay-store.js';
4
+ import { discoverLocalRelayStoreSnapshots, relayStoreDirectory } from './relay-store.js';
7
5
  import { realpathSync } from 'node:fs';
8
6
  import { once } from 'node:events';
9
7
  import { fileURLToPath } from 'node:url';
@@ -489,7 +487,7 @@ export async function createService(args) {
489
487
  let overviewProvider;
490
488
  if (repo) {
491
489
  overviewProvider = async () => {
492
- const snapshots = await discoverLocalRelayStoreSnapshots(join(homedir(), '.agents-relay', 'stores'));
490
+ const snapshots = await discoverLocalRelayStoreSnapshots(relayStoreDirectory());
493
491
  return dashboardManagedJobs(snapshots.flatMap(snapshot => snapshot.jobs
494
492
  .filter(job => job.repository === repo)
495
493
  .map(job => ({
@@ -1,7 +1,12 @@
1
1
  import { createHash } from 'node:crypto';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
2
4
  /** Primary local store key is immutable Job ID, independent of a PR number. */
3
5
  export function relayJobStorePath(directory, jobId) {
4
6
  if (!jobId.trim() || jobId.length > 1024)
5
7
  throw new Error('Invalid immutable Job ID');
6
8
  return directory + '/job-' + createHash('sha256').update(jobId).digest('hex') + '.db';
7
9
  }
10
+ export function relayStoreDirectory(env = process.env) {
11
+ return env.AGENTS_RELAY_STORE_DIR || join(homedir(), '.agents-relay', 'stores');
12
+ }
@@ -1,16 +1,14 @@
1
- import { homedir } from 'node:os';
2
- import { join } from 'node:path';
3
1
  import { GhApiClient } from './github-client.js';
4
2
  import { DisabledGitHubClient, noGitHubMode } from './no-github.js';
5
3
  import { GitHubStore } from './store.js';
6
4
  import { LocalAuthorityDurableStore } from './local-authority-store.js';
7
- import { relayStorePath, relayJobStorePath, SQLiteRelayStore } from './relay-store.js';
5
+ import { relayStoreDirectory, relayStorePath, relayJobStorePath, SQLiteRelayStore } from './relay-store.js';
8
6
  /** Open the canonical V2 SQLite authority without making any GitHub request. */
9
7
  export function openLocalManagedStore(repository, prNumber, options = {}) {
10
8
  if (!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(repository) || repository.includes('..') || !Number.isInteger(prNumber) || prNumber < 0 || (prNumber === 0 && !options.jobId)) {
11
9
  throw new Error('Local managed store requires a repository and either a positive PR number or immutable jobId');
12
10
  }
13
- const directory = options.directory ?? join(homedir(), '.agents-relay', 'stores');
11
+ const directory = options.directory ?? relayStoreDirectory();
14
12
  const relay = new SQLiteRelayStore(prNumber === 0 ? relayJobStorePath(directory, options.jobId) : relayStorePath(directory, repository, prNumber));
15
13
  const client = prNumber === 0 || options.noGitHub || noGitHubMode() ? new DisabledGitHubClient() : options.client ?? new GhApiClient();
16
14
  const github = new GitHubStore(client, repository, prNumber, options.trustedAuthors ?? new Set());
@@ -367,7 +367,7 @@ export class SQLiteRelayStore {
367
367
  export function relayStorePath(directory, repository, prNumber) {
368
368
  return directory + '/' + `${repository.replace('/', '_')}-${prNumber}.db`;
369
369
  }
370
- export { relayJobStorePath } from './local-job-path.js';
370
+ export { relayJobStorePath, relayStoreDirectory } from './local-job-path.js';
371
371
  /**
372
372
  * SQLite is the only job registry. Inventory queries must be strictly read-only:
373
373
  * opening a writable RelayStore here would reset in-flight projection claims
package/dist/relayd.js CHANGED
@@ -13,7 +13,7 @@ import { Reconciler } from './reconciler.js';
13
13
  import { dashboardManagedJobs, GitHubStore, InMemoryStore, resolvePullRequest } from './store.js';
14
14
  import { GitHubProjectionWriter } from './projected-store.js';
15
15
  import { LocalAuthorityDurableStore } from './local-authority-store.js';
16
- import { discoverLocalRelayStoreSnapshots, relayStorePath, SQLiteRelayStore } from './relay-store.js';
16
+ import { discoverLocalRelayStoreSnapshots, relayStoreDirectory, relayStorePath, SQLiteRelayStore } from './relay-store.js';
17
17
  import { createJobIdRegistryResolver } from './local-job-registry.js';
18
18
  import { projectAuthoritativeLocalMerge } from './local-merge-projection.js';
19
19
  import { archiveDisplayPullRequest } from './github-pr-archive.js';
@@ -116,7 +116,7 @@ export async function runDaemon(argv) {
116
116
  const key = `${repository}#${prNumber}`;
117
117
  let relay = relayStores.get(key);
118
118
  if (!relay) {
119
- relay = new SQLiteRelayStore(relayStorePath(join(homedir(), '.agents-relay', 'stores'), repository, prNumber));
119
+ relay = new SQLiteRelayStore(relayStorePath(relayStoreDirectory(), repository, prNumber));
120
120
  relayStores.set(key, relay);
121
121
  }
122
122
  const github = new GitHubStore(client, repository, prNumber, trusted, seed);
@@ -127,7 +127,7 @@ export async function runDaemon(argv) {
127
127
  return projected;
128
128
  };
129
129
  // SQLite inventory, not GitHub discovery, defines which repositories exist.
130
- const existingInventory = await discoverLocalRelayStoreSnapshots(join(homedir(), '.agents-relay', 'stores'));
130
+ const existingInventory = await discoverLocalRelayStoreSnapshots(relayStoreDirectory());
131
131
  for (const snapshot of existingInventory)
132
132
  for (const job of snapshot.jobs) {
133
133
  if (!repositories.includes(job.repository))
@@ -136,7 +136,7 @@ export async function runDaemon(argv) {
136
136
  if (repositories.length === 0)
137
137
  repositories.push('local/' + basename(workspaceRoot));
138
138
  const inventoryFromLocal = async () => {
139
- const snapshots = await discoverLocalRelayStoreSnapshots(join(homedir(), '.agents-relay', 'stores'));
139
+ const snapshots = await discoverLocalRelayStoreSnapshots(relayStoreDirectory());
140
140
  const known = new Set(repositories);
141
141
  const entries = snapshots.flatMap(snapshot => snapshot.jobs.filter(job => known.has(job.repository)).map(job => ({
142
142
  job, githubState: snapshot.facts?.state ?? 'UNKNOWN',
@@ -174,7 +174,7 @@ export async function runDaemon(argv) {
174
174
  };
175
175
  };
176
176
  const refreshPullRequest = async (repository, prNumber, trigger, options) => (await refreshPullRequestSnapshot(repository, prNumber, trigger, options)).overviews;
177
- const localJobBinding = createJobIdRegistryResolver(join(homedir(), '.agents-relay', 'stores'), relayStores, { client, trustedAuthors: trusted, noGitHub });
177
+ const localJobBinding = createJobIdRegistryResolver(relayStoreDirectory(), relayStores, { client, trustedAuthors: trusted, noGitHub });
178
178
  const requireLocalJobBinding = async (jobId) => {
179
179
  const binding = await localJobBinding(jobId);
180
180
  if (!binding)
@@ -214,7 +214,7 @@ export async function runDaemon(argv) {
214
214
  return;
215
215
  if (client.rateLimitStatus().limited)
216
216
  return;
217
- const snapshots = await discoverLocalRelayStoreSnapshots(join(homedir(), '.agents-relay', 'stores'));
217
+ const snapshots = await discoverLocalRelayStoreSnapshots(relayStoreDirectory());
218
218
  const bindingCandidates = orderGitHubBindingCandidates(snapshots.flatMap(snapshot => snapshot.jobs.map(job => ({ snapshotPath: snapshot.path, job }))));
219
219
  for (const candidate of bindingCandidates) {
220
220
  const snapshot = { path: candidate.snapshotPath };
@@ -1,9 +1,7 @@
1
- import { homedir } from 'node:os';
2
- import { join } from 'node:path';
3
1
  import { eventFor } from './events.js';
4
- import { SQLiteRelayStore, relayStorePath } from './relay-store.js';
2
+ import { SQLiteRelayStore, relayStoreDirectory, relayStorePath } from './relay-store.js';
5
3
  /** Single-Job service uses the same durable publish facts as the repository daemon. */
6
- export async function handleServicePublication(job, wake, bus, directory = join(homedir(), '.agents-relay', 'stores')) {
4
+ export async function handleServicePublication(job, wake, bus, directory = relayStoreDirectory()) {
7
5
  if (!wake.publication && !wake.publishFailure)
8
6
  return;
9
7
  const sqlite = new SQLiteRelayStore(relayStorePath(directory, job.repository, job.prNumber));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agents-relay",
3
- "version": "2.0.69",
3
+ "version": "2.0.70",
4
4
  "description": "Durable async agent jobs coordinated through GitHub pull requests",
5
5
  "type": "module",
6
6
  "bin": {
@@ -12,9 +12,9 @@
12
12
  "test": "npm run test:full",
13
13
  "test:chatgpt-temp-quality": "AGENTS_RELAY_RUN_CHATGPT_TEMP_QUALITY=1 sh -c \"npm run build && node test/quality/run-temporary-chat.mjs\"",
14
14
  "dev": "tsx src/cli.ts",
15
- "test:publish": "npm run build && node --test --test-concurrency=1 --test-force-exit test/*.test.js",
16
- "test:fast": "npm run build && node --test --test-concurrency=2 test/workflow-lifecycle.test.js test/workflow-v2-lifecycle-parity.test.js test/workflow-effects.test.js test/relay-store.test.js test/local-authority.test.js test/local-jobid-routing.test.js test/local-sqlite-jobid.test.js test/no-github.test.js test/scheduled-jobs.test.js test/reconcile-coordinator.test.js test/service-publication.test.js test/github-webhook.test.js",
17
- "test:full": "npm run build && node --test --test-concurrency=1 test/*.test.js",
15
+ "test:publish": "npm run build && node --import ./test/test-store-env.mjs --test --test-concurrency=1 --test-force-exit test/*.test.js",
16
+ "test:fast": "npm run build && node --import ./test/test-store-env.mjs --test --test-concurrency=2 test/workflow-lifecycle.test.js test/workflow-v2-lifecycle-parity.test.js test/workflow-effects.test.js test/relay-store.test.js test/local-authority.test.js test/local-jobid-routing.test.js test/local-sqlite-jobid.test.js test/no-github.test.js test/scheduled-jobs.test.js test/reconcile-coordinator.test.js test/service-publication.test.js test/github-webhook.test.js",
17
+ "test:full": "npm run build && node --import ./test/test-store-env.mjs --test --test-concurrency=1 test/*.test.js",
18
18
  "e2e": "node e2e/runner/run.mjs"
19
19
  },
20
20
  "devDependencies": {