agents-relay 1.0.111 → 1.0.112

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-help.js CHANGED
@@ -44,7 +44,7 @@ Example:
44
44
 
45
45
  Required: --repo OWNER/REPO, --id JOB_ID, and either --head BRANCH or --pr NUMBER.
46
46
  With --head, reuse/create the PR for that head. With --pr, bind an existing unmanaged PR.
47
- Common: --base BRANCH (default: main), --title TEXT, --objective-file PATH|- (preferred), --objective MARKDOWN (short/simple only), --priority P0|P1|P2|P3, --mode fixed|autonomous, --no-auto-merge, --no-review. --objective and --objective-file are mutually exclusive. For a new --head Job, one is required and must contain enough standalone context to complete the Job without the originating chat/thread. Objective Markdown is also the default PR body; --body is an explicit override.
47
+ Common: --base BRANCH (default: main), --title TEXT, --objective-file PATH|- (preferred), --objective MARKDOWN (short/simple only), --project-path RELATIVE_PATH, --priority P0|P1|P2|P3, --mode fixed|autonomous, --no-auto-merge, --no-review. --objective and --objective-file are mutually exclusive. For a new --head Job, one is required and must contain enough standalone context to complete the Job without the originating chat/thread. Objective Markdown is also the default PR body; --body is an explicit override.
48
48
  Job creation never creates executable tasks. The orchestrator decides the initial task batch separately. Re-running create against an already healthy Job is idempotent and does not mutate its metadata.
49
49
 
50
50
  Example:
@@ -52,7 +52,7 @@ Example:
52
52
  'job update': `Partially update mutable metadata on a healthy existing Job.
53
53
 
54
54
  Required: --repo OWNER/REPO, --pr NUMBER, and --id JOB_ID.
55
- Mutable: --title, --objective-file PATH|- (preferred) or --objective MARKDOWN, --priority, --mode fixed|autonomous, --schedule, --auto-merge|--no-auto-merge, --review|--no-review, and continuation options. Updating the objective also updates the PR body.
55
+ Mutable: --title, --objective-file PATH|- (preferred) or --objective MARKDOWN, --project-path RELATIVE_PATH, --priority, --mode fixed|autonomous, --schedule, --auto-merge|--no-auto-merge, --review|--no-review, and continuation options. Updating the objective also updates the PR body.
56
56
  Unspecified fields are preserved. Marker/binding corruption is never repaired implicitly; update fails with a precise diagnostic instead.
57
57
 
58
58
  Example:
package/dist/cli.js CHANGED
@@ -18,7 +18,7 @@ import { AgentObjectivePlanner, CommandObjectivePlanner } from './planner.js';
18
18
  import { extendRetryBudget, transitionJobEvent, transitionTaskEvent } from './scheduler.js';
19
19
  import { dueBuckets, processDueScheduledJobs, syncScheduleLabels } from './scheduled-jobs.js';
20
20
  import { resolveExecutionContext } from './execution-context.js';
21
- import { defaultWorkspaceRoot, resolveManagedProjectPath } from './workspace.js';
21
+ import { defaultWorkspaceRoot, resolveManagedProjectPath, resolveRepositoryProjectPath } from './workspace.js';
22
22
  import { runBlockerCommand } from './cli-blocker.js';
23
23
  import { helpText, usage } from './cli-help.js';
24
24
  export { helpText } from './cli-help.js';
@@ -44,6 +44,7 @@ export async function ensureManagedGitHubJob(client, repositoryName, trustedAuth
44
44
  id: options.id,
45
45
  title: options.title || pr.title || 'Agents Relay job',
46
46
  objective: options.objective || pr.body || undefined,
47
+ projectPath: options.projectPath,
47
48
  description: pr.body || undefined,
48
49
  priority: options.priority ?? 'P2',
49
50
  executionMode: options.executionMode ?? 'fixed',
@@ -323,6 +324,7 @@ async function runJobCommand(action, args) {
323
324
  id,
324
325
  title: arg(args, '--title') || undefined,
325
326
  objective: objective || undefined,
327
+ projectPath: arg(args, '--project-path') || undefined,
326
328
  priority: priority(args),
327
329
  executionMode: requestedExecutionMode(args),
328
330
  schedule: scheduleFromArgs(args),
@@ -382,6 +384,8 @@ async function runJobCommand(action, args) {
382
384
  throw new Error('Job objective cannot be empty');
383
385
  job.objective = objective;
384
386
  }
387
+ if (hasArg(args, '--project-path'))
388
+ job.projectPath = arg(args, '--project-path') || undefined;
385
389
  if (hasArg(args, '--priority'))
386
390
  job.priority = priority(args, job.priority ?? 'P2');
387
391
  if (hasArg(args, '--mode') || hasArg(args, '--execution-mode'))
@@ -414,7 +418,7 @@ async function runJobCommand(action, args) {
414
418
  }
415
419
  export function runtimePlanner(args) { const plannerCommand = arg([...args], '--planner-command'); const modelRuntime = arg([...args], '--codex', 'codex'); return plannerCommand ? new CommandObjectivePlanner(plannerCommand) : new AgentObjectivePlanner(modelRuntime); }
416
420
  export function runtimeReviewer() { return { requirements: { capabilities: ['model'] } }; }
417
- export function runtime(store, args, bus, usageRegistry = codexAndZaiUsageRegistry()) { const emit = (event) => { process.stderr.write(`${JSON.stringify(event)}\n`); }; const modelRuntime = arg(args, '--codex', 'codex'); const policy = chatGptTabClosePolicy(args); const planner = runtimePlanner(args); return new Reconciler(store, { owner: arg(args, '--owner', `cli-${process.pid}`), maxConcurrent: Number(arg(args, '--concurrency', '4')), leaseMs: Number(arg(args, '--lease-ms', '300000')), adapters: [new CodexAdapter(modelRuntime), new ChatGptAdapter({ tabClosePolicy: policy })], continuations: [new CodexThreadContinuation(modelRuntime), new CommandContinuation(), new WebhookContinuation()], planner, usageSnapshotProvider: () => usageRegistry.snapshot(), decisionEngine: new LayaCliDecisionEngine(), decisionMode: decisionMode(), reviewer: runtimeReviewer(), eventBus: bus, emit, chatgptTabClosePolicy: policy, projectPathForJob: job => resolveManagedProjectPath(job.repository, arg(args, '--project-path'), arg(args, '--workspace-root') || defaultWorkspaceRoot()) }); }
421
+ export function runtime(store, args, bus, usageRegistry = codexAndZaiUsageRegistry()) { const emit = (event) => { process.stderr.write(`${JSON.stringify(event)}\n`); }; const modelRuntime = arg(args, '--codex', 'codex'); const policy = chatGptTabClosePolicy(args); const planner = runtimePlanner(args); return new Reconciler(store, { owner: arg(args, '--owner', `cli-${process.pid}`), maxConcurrent: Number(arg(args, '--concurrency', '4')), leaseMs: Number(arg(args, '--lease-ms', '300000')), adapters: [new CodexAdapter(modelRuntime), new ChatGptAdapter({ tabClosePolicy: policy })], continuations: [new CodexThreadContinuation(modelRuntime), new CommandContinuation(), new WebhookContinuation()], planner, usageSnapshotProvider: () => usageRegistry.snapshot(), decisionEngine: new LayaCliDecisionEngine(), decisionMode: decisionMode(), reviewer: runtimeReviewer(), eventBus: bus, emit, chatgptTabClosePolicy: policy, projectPathForJob: async (job) => resolveRepositoryProjectPath(await resolveManagedProjectPath(job.repository, arg(args, '--project-path'), arg(args, '--workspace-root') || defaultWorkspaceRoot()), job.projectPath) }); }
418
422
  export function dashboardTaskAction(store, reconciler, action) {
419
423
  return async (jobId, taskId) => {
420
424
  const job = await store.load(jobId);
package/dist/planner.js CHANGED
@@ -147,7 +147,7 @@ export function plannerInput(context) {
147
147
  boundaries: agent.restrictions,
148
148
  runtime: agent.runtime,
149
149
  }));
150
- return JSON.stringify({ objective: context.objective, previous_planner_task_id: context.previousPlannerTaskId, visible_agents: visibleAgents ?? [], execution_context: context.executionContext, tasks: context.tasks.map(task => ({ id: task.id, kind: task.kind ?? 'work', parent_task_id: task.parentTaskId, state: task.state, input: task.input, result: task.result, planner_result: task.plannerResult, error: task.error })) });
150
+ return JSON.stringify({ objective: context.objective, retry_feedback: context.retryFeedback, previous_planner_task_id: context.previousPlannerTaskId, visible_agents: visibleAgents ?? [], execution_context: context.executionContext, tasks: context.tasks.map(task => ({ id: task.id, kind: task.kind ?? 'work', parent_task_id: task.parentTaskId, state: task.state, input: task.input, result: task.result, planner_result: task.plannerResult, error: task.error })) });
151
151
  }
152
152
  export class CommandObjectivePlanner {
153
153
  command;
@@ -444,7 +444,7 @@ export class Reconciler extends ReconcilerReviewBase {
444
444
  let promise;
445
445
  try {
446
446
  const executionContext = await this.resolvedContext(job, task, 'planner');
447
- promise = planner.plan({ job, tasks: job.tasks, objective: job.objective ?? job.description ?? job.title, previousPlannerTaskId: task.parentTaskId, executionContext }, controller.signal);
447
+ promise = planner.plan({ job, tasks: job.tasks, objective: job.objective ?? job.description ?? job.title, previousPlannerTaskId: task.parentTaskId, executionContext, retryFeedback: task.error ?? undefined }, controller.signal);
448
448
  }
449
449
  catch (error) {
450
450
  await this.finishPlanner(job.id, task.id, executionId, error instanceof Error ? error : new Error(String(error)));
package/dist/relayd.js CHANGED
@@ -13,7 +13,7 @@ import { dashboardManagedJobs, GitHubStore, InMemoryStore, loadManagedGitHubPull
13
13
  import { githubAuthContext } from './github-auth.js';
14
14
  import { dashboardJobAction, dashboardTaskAction, isEntrypoint, runtimePlanner, runtimeReviewer, startService, SERVICE_DEFAULTS } from './cli.js';
15
15
  import { codexAndZaiUsageRegistry } from './usage.js';
16
- import { defaultWorkspaceRoot, discoverWorkspaceRepositories, resolveManagedProjectPath } from './workspace.js';
16
+ import { defaultWorkspaceRoot, discoverWorkspaceRepositories, resolveManagedProjectPath, resolveRepositoryProjectPath } from './workspace.js';
17
17
  import { deriveJobRuntimeStatus } from './runtime-status.js';
18
18
  import { decisionMode, LayaCliDecisionEngine } from './decision.js';
19
19
  import { JsonlDecisionAuditStore } from './decision-audit.js';
@@ -76,7 +76,7 @@ export async function runDaemon(argv) {
76
76
  const usageRegistry = codexAndZaiUsageRegistry();
77
77
  const decisionAuditStore = new JsonlDecisionAuditStore(process.env.AGENTS_RELAY_DECISION_AUDIT_PATH ?? join(homedir(), '.agents-relay', 'decision-audit.jsonl'));
78
78
  let cachedOverviews = [];
79
- const makeReconciler = (store, maxConcurrent) => new Reconciler(store, { 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: new LayaCliDecisionEngine(), decisionMode: decisionMode(), decisionAuditStore, eventBus: bus, reviewer: runtimeReviewer(), projectPathForJob: job => repositoryPaths.get(job.repository) ?? resolveManagedProjectPath(job.repository, explicitProjectPath, workspaceRoot) });
79
+ const makeReconciler = (store, maxConcurrent) => new Reconciler(store, { 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: new LayaCliDecisionEngine(), decisionMode: decisionMode(), decisionAuditStore, eventBus: bus, reviewer: runtimeReviewer(), projectPathForJob: async (job) => resolveRepositoryProjectPath(repositoryPaths.get(job.repository) ?? await resolveManagedProjectPath(job.repository, explicitProjectPath, workspaceRoot), job.projectPath) });
80
80
  if (repositories.length === 0)
81
81
  throw new Error(`No Git repositories with GitHub origins found under ${normalized.workspaceRoot ?? defaultWorkspaceRoot()}`);
82
82
  const pool = new RepositoryWorkerPool(client, repositories, trusted, concurrency, makeReconciler);
package/dist/workspace.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { mkdir, readdir, stat } from 'node:fs/promises';
2
- import { dirname, join, relative, resolve } from 'node:path';
2
+ import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
3
3
  import { homedir } from 'node:os';
4
4
  import { execFile } from 'node:child_process';
5
5
  import { promisify } from 'node:util';
@@ -75,11 +75,24 @@ export async function resolveManagedProjectPath(repository, explicitProjectPath
75
75
  return resolve(explicitProjectPath);
76
76
  return repository ? resolveWorkspaceRepositoryPath(repository, root) : resolve(cwd);
77
77
  }
78
+ export function resolveRepositoryProjectPath(repositoryRoot, projectPath) {
79
+ const root = resolve(repositoryRoot);
80
+ if (!projectPath)
81
+ return root;
82
+ if (isAbsolute(projectPath))
83
+ throw new Error('Job projectPath must be repository-relative');
84
+ const target = resolve(root, projectPath);
85
+ const rel = relative(root, target);
86
+ if (rel === '..' || rel.startsWith('..' + sep) || isAbsolute(rel))
87
+ throw new Error('Job projectPath escapes repository root');
88
+ return target;
89
+ }
78
90
  async function git(path, args) {
79
91
  return (await execFileAsync('git', ['-C', path, ...args])).stdout.trim();
80
92
  }
81
93
  export async function prepareManagedExecutionWorktree(projectPath, repository, prNumber, head, executionId) {
82
94
  const primary = await git(projectPath, ['rev-parse', '--show-toplevel']);
95
+ const projectRelative = (await git(projectPath, ['rev-parse', '--show-prefix'])).replace(/\/$/, '');
83
96
  const remote = await git(primary, ['config', '--get', 'remote.origin.url']);
84
97
  if (githubRepositoryFromRemote(remote) !== repository)
85
98
  throw new Error(`Primary checkout ${primary} does not match ${repository}`);
@@ -96,7 +109,7 @@ export async function prepareManagedExecutionWorktree(projectPath, repository, p
96
109
  const existingHead = await git(worktree, ['rev-parse', 'HEAD']);
97
110
  if (resolve(existingRoot) !== resolve(worktree) || existingHead !== head.sha)
98
111
  throw new Error(`Existing execution worktree ${worktree} is not the expected PR head`);
99
- return worktree;
112
+ return projectRelative ? join(worktree, projectRelative) : worktree;
100
113
  }
101
114
  catch (error) {
102
115
  try {
@@ -104,7 +117,7 @@ export async function prepareManagedExecutionWorktree(projectPath, repository, p
104
117
  }
105
118
  catch {
106
119
  await git(primary, ['worktree', 'add', '--detach', worktree, head.sha]);
107
- return worktree;
120
+ return projectRelative ? join(worktree, projectRelative) : worktree;
108
121
  }
109
122
  throw error;
110
123
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agents-relay",
3
- "version": "1.0.111",
3
+ "version": "1.0.112",
4
4
  "description": "Durable async agent jobs coordinated through GitHub pull requests",
5
5
  "type": "module",
6
6
  "bin": {