agents-relay 2.0.174 → 2.0.176

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.
@@ -0,0 +1,40 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { Ajv } from 'ajv';
3
+ /** Explicit, read-only validation. Projects own schemas and decide when to call it. */
4
+ export async function validateArtifact(artifactPath, schemaPath) {
5
+ if (!artifactPath || !schemaPath)
6
+ throw new Error('artifact validate requires --artifact and --schema');
7
+ const [artifactText, schemaText] = await Promise.all([
8
+ readFile(artifactPath, 'utf8'),
9
+ readFile(schemaPath, 'utf8'),
10
+ ]);
11
+ const artifact = JSON.parse(artifactText);
12
+ const schema = JSON.parse(schemaText);
13
+ if (!schema || typeof schema !== 'object' || Array.isArray(schema))
14
+ throw new Error('Schema must be a JSON object');
15
+ if ('$async' in schema)
16
+ throw new Error('Async schemas are not supported');
17
+ const validate = new Ajv({ allErrors: true, strict: false }).compile(schema);
18
+ const valid = validate(artifact);
19
+ if (typeof valid !== 'boolean')
20
+ throw new Error('Async validation is not supported');
21
+ return {
22
+ valid,
23
+ errors: (validate.errors ?? []).map(error => ({
24
+ path: error.instancePath || '/',
25
+ message: error.message ?? 'invalid value',
26
+ })),
27
+ };
28
+ }
29
+ export async function runArtifactCommand(action, args) {
30
+ if (action !== 'validate')
31
+ throw new Error('artifact supports only validate');
32
+ const value = (flag) => {
33
+ const index = args.indexOf(flag);
34
+ return index < 0 ? '' : args[index + 1] ?? '';
35
+ };
36
+ const result = await validateArtifact(value('--artifact'), value('--schema'));
37
+ console.log(JSON.stringify(result, null, 2));
38
+ if (!result.valid)
39
+ throw new Error('Artifact validation failed');
40
+ }
package/dist/cli-help.js CHANGED
@@ -1,4 +1,4 @@
1
- export const usage = 'agents-relay <job|task|review|plan|blocker|projection|escalation|context|init|record|agent-register|agent-discover|status|reconcile|serve> [options]';
1
+ export const usage = 'agents-relay <job|task|review|artifact|plan|blocker|projection|escalation|context|init|record|agent-register|agent-discover|status|reconcile|serve> [options]';
2
2
  const help = {
3
3
  root: `Agents Relay coordinates durable asynchronous agent jobs through local SQLite and workflow graphs.
4
4
 
@@ -11,6 +11,7 @@ Commands:
11
11
  job create|update|show|list|merge|unblock|cancel|pause|resume Manage SQLite-authoritative jobs with optional GitHub PRs
12
12
  task create|update|show|list|classify|complete|retry|unblock|cancel Manage durable tasks
13
13
  review request Request graph-owned review (optional thread override)
14
+ artifact validate Check a JSON artifact against a project-owned JSON Schema
14
15
  plan Invoke the Planner for an existing managed Job
15
16
  plan apply Submit a validated Task Tree within one active Plan
16
17
  blocker add|resolve|show Manage cross-Job blocker dependencies
@@ -60,6 +61,10 @@ Request a new SHA-bound reviewer turn through the existing Relay lifecycle.
60
61
  Usage: npx agents-relay review request --repo OWNER/REPO --pr NUMBER --id JOB_ID [--thread-id THREAD_ID]
61
62
 
62
63
  When provided, --thread-id persists a reviewer destination; otherwise the Job reviewer thread, then creator thread, then legacy review route is used. The command wakes Relay. Review Task creation, idempotency, routing and delivery remain graph/daemon-owned. Rejects in-flight/queued reviews; does not itself mark review delivered.`,
64
+ artifact: `Check a JSON artifact against a project-owned JSON Schema. Read-only; does not affect Task state or planning.
65
+
66
+ Usage: agents-relay artifact validate --artifact PATH --schema PATH
67
+ Exit 0 on PASS, 1 on schema mismatch or invalid input. Returns JSON { valid, errors }.`,
63
68
  'review run': `Evaluate the calling Agent's requested outcome inside its Worker. A generic Outcome Reviewer runs by default; --agents adds comma-separated professional roles found in the project or shared agent catalog. Does not create Relay Tasks or trigger Final Review.
64
69
 
65
70
  Usage: agents-relay review run (--prompt TEXT | --prompt-file PATH) [--project-path DIR] [--agents role,role] [--output PATH]
package/dist/cli.js CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { runArtifactCommand } from './cli-artifact.js';
2
3
  import { runWorkflowAction } from './cli-workflow-action.js';
3
4
  import { workflowCliResult } from './cli-workflow.js';
4
5
  import { configureLocalGitMerge } from './cli-local-merge-config.js';
@@ -743,6 +744,11 @@ async function main() {
743
744
  await runJobCommand(action ?? '', jobArgs);
744
745
  return;
745
746
  }
747
+ if (command === 'artifact') {
748
+ const [action, ...artifactArgs] = args;
749
+ await runArtifactCommand(action ?? '', artifactArgs);
750
+ return;
751
+ }
746
752
  if (command === 'task') {
747
753
  const [action, ...taskArgs] = args;
748
754
  await runTaskCommand(action ?? '', taskArgs);
package/dist/planner.js CHANGED
@@ -106,7 +106,6 @@ export function materializePlannerChild(job, planner, spec, executionContext) {
106
106
  const round = 'round' in planner ? planner.round : planner.planningRound ?? 1;
107
107
  const plannerHeadSha = 'round' in planner ? planner.headSha : undefined;
108
108
  const reviewHeadSha = spec.agentName === 'reviewer' && !(spec.dependencies?.length) ? plannerHeadSha ?? job.headSha : undefined;
109
- const objectiveRoot = spec.agentName === 'reviewer' ? job.tasks.find(task => task.kind !== 'planner' && task.agentName !== 'reviewer' && task.agentName !== 'troubleshooter' && !task.parentTaskId) : undefined;
110
109
  const resolution = spec.resolution;
111
110
  if (resolution) {
112
111
  const target = resolution.targetTaskId ? job.tasks.find(task => task.id === resolution.targetTaskId) : undefined;
@@ -114,7 +113,7 @@ export function materializePlannerChild(job, planner, spec, executionContext) {
114
113
  throw new Error(`Planner resolution for ${spec.id} must target one unresolved durable Task`);
115
114
  }
116
115
  const reviewerThread = spec.agentName === 'reviewer' ? prepareReviewerThreadDelivery(job, { id: spec.id, agentName: spec.agentName, reviewHeadSha }) : undefined, implementationThread = (spec.agentName === 'implementation' || spec.agentName == null) && job.implementationThreadId ? job.implementationThreadId : undefined, threadOwned = Boolean(reviewerThread || implementationThread);
117
- return createTaskEntity(job, { jobId: job.id, id: spec.id, title: spec.agentName === 'reviewer' ? 'Review' : spec.title, kind: 'work', priority: spec.priority ?? job.priority, storySize: spec.storySize ?? 'M', projectName: spec.projectName, agentName: spec.agentName, agentGraph: spec.agentGraph, review: spec.review, parentTaskId: objectiveRoot?.id ?? spec.parentTaskId ?? null, dependencies: spec.dependencies ?? [], capabilities: spec.capabilities ?? [], workerRequirements: threadOwned ? chatgptThreadRequirements(spec.workerRequirements) : spec.workerRequirements, adapter: threadOwned ? 'chatgpt' : (spec.adapter ?? 'codex'), chatgptThreadDelivery: reviewerThread, input: spec.input, output: spec.output, continuation: spec.continuation ?? null, continuationDeliveredAt: null, attempt: 0, maxAttempts: spec.maxAttempts ?? 3, leaseOwner: null, leaseExpiresAt: null, executionId: null, threadId: null, result: null, plannerResult: null, error: null, timeoutMs: spec.timeoutMs ?? taskOverallTimeoutMs(spec.storySize ?? 'M'), createdAt: now, updatedAt: now, planningDecisionId: decisionId, planningSource: source, planningRound: round, reviewHeadSha, resolution: resolution ? { ...resolution, state: 'proposed', owner: 'planner', updatedAt: now } : undefined });
116
+ return createTaskEntity(job, { jobId: job.id, id: spec.id, title: spec.agentName === 'reviewer' ? 'Review' : spec.title, kind: 'work', priority: spec.priority ?? job.priority, storySize: spec.storySize ?? 'M', projectName: spec.projectName, agentName: spec.agentName, agentGraph: spec.agentGraph, review: spec.review, parentTaskId: spec.agentName === 'reviewer' ? null : spec.parentTaskId ?? null, dependencies: spec.dependencies ?? [], capabilities: spec.capabilities ?? [], workerRequirements: threadOwned ? chatgptThreadRequirements(spec.workerRequirements) : spec.workerRequirements, adapter: threadOwned ? 'chatgpt' : (spec.adapter ?? 'codex'), chatgptThreadDelivery: reviewerThread, input: spec.input, output: spec.output, continuation: spec.continuation ?? null, continuationDeliveredAt: null, attempt: 0, maxAttempts: spec.maxAttempts ?? 3, leaseOwner: null, leaseExpiresAt: null, executionId: null, threadId: null, result: null, plannerResult: null, error: null, timeoutMs: spec.timeoutMs ?? taskOverallTimeoutMs(spec.storySize ?? 'M'), createdAt: now, updatedAt: now, planningDecisionId: decisionId, planningSource: source, planningRound: round, reviewHeadSha, resolution: resolution ? { ...resolution, state: 'proposed', owner: 'planner', updatedAt: now } : undefined });
118
117
  }
119
118
  export function planningSpec(task) {
120
119
  if (!task.output)
@@ -153,7 +153,7 @@ export class ReconcilerReviewBase extends ReconcilerRuntimeBase {
153
153
  ].join('\n\n');
154
154
  const task = createTaskEntity(job, {
155
155
  jobId: job.id, id, title: 'Review', kind: 'work', priority: job.priority, storySize: 'M', projectName: job.title, agentName: 'reviewer',
156
- parentTaskId: reviewGateTasks.find(item => !item.parentTaskId)?.id ?? null, dependencies: reviewGateTasks.map(item => item.id), capabilities: ['review'],
156
+ parentTaskId: null, dependencies: reviewGateTasks.map(item => item.id), capabilities: ['review'],
157
157
  workerSelection: reviewerRouting.selection, adapter: reviewerRouting.candidate.adapter,
158
158
  input,
159
159
  output: { kind: 'task_pr' }, routing: reviewerRouting.candidate.routing, continuation: null, continuationDeliveredAt: null,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agents-relay",
3
- "version": "2.0.174",
3
+ "version": "2.0.176",
4
4
  "description": "Durable async agent jobs coordinated through GitHub pull requests",
5
5
  "repository": {
6
6
  "type": "git",
@@ -45,5 +45,8 @@
45
45
  "workflow",
46
46
  "README.md",
47
47
  "LICENSE"
48
- ]
48
+ ],
49
+ "dependencies": {
50
+ "ajv": "^8.20.0"
51
+ }
49
52
  }