From 75337dd25ba60f8739f5a6563b4b309682f7fadd Mon Sep 17 00:00:00 2001 From: Khaliq Date: Wed, 4 Mar 2026 17:22:12 -0800 Subject: [PATCH 1/6] path and workdir --- packages/sdk-py/src/agent_relay/__init__.py | 2 + packages/sdk-py/src/agent_relay/types.py | 33 ++++++++++++++++ packages/sdk/src/workflows/schema.json | 44 +++++++++++++++++++++ packages/sdk/src/workflows/types.ts | 26 ++++++++++++ 4 files changed, 105 insertions(+) diff --git a/packages/sdk-py/src/agent_relay/__init__.py b/packages/sdk-py/src/agent_relay/__init__.py index 8ff826dd9..dd247ec30 100644 --- a/packages/sdk-py/src/agent_relay/__init__.py +++ b/packages/sdk-py/src/agent_relay/__init__.py @@ -59,6 +59,7 @@ SwarmPattern, AgentDefinition, AgentConstraints, + PathDefinition, RelayYamlConfig, AgentCli, IdleNudgeConfig, @@ -128,6 +129,7 @@ "SwarmPattern", "AgentDefinition", "AgentConstraints", + "PathDefinition", "RelayYamlConfig", "AgentCli", "IdleNudgeConfig", diff --git a/packages/sdk-py/src/agent_relay/types.py b/packages/sdk-py/src/agent_relay/types.py index badc3e85e..c33cb175c 100644 --- a/packages/sdk-py/src/agent_relay/types.py +++ b/packages/sdk-py/src/agent_relay/types.py @@ -123,6 +123,24 @@ def to_dict(self) -> dict[str, Any]: return result +@dataclass +class PathDefinition: + """A named path to an external directory for cross-repo workflows.""" + + name: str + path: str + description: str | None = None + required: bool | None = None + + def to_dict(self) -> dict[str, Any]: + result: dict[str, Any] = {"name": self.name, "path": self.path} + if self.description is not None: + result["description"] = self.description + if self.required is not None: + result["required"] = self.required + return result + + @dataclass class AgentDefinition: name: str @@ -132,6 +150,9 @@ class AgentDefinition: channels: list[str] | None = None constraints: AgentConstraints | None = None interactive: bool | None = None + cwd: str | None = None + workdir: str | None = None + additional_paths: list[str] | None = None def to_dict(self) -> dict[str, Any]: result: dict[str, Any] = { @@ -150,6 +171,12 @@ def to_dict(self) -> dict[str, Any]: result["constraints"] = constraints if self.interactive is not None: result["interactive"] = self.interactive + if self.cwd is not None: + result["cwd"] = self.cwd + if self.workdir is not None: + result["workdir"] = self.workdir + if self.additional_paths is not None: + result["additionalPaths"] = self.additional_paths return result @@ -175,6 +202,7 @@ class WorkflowStep: verification: VerificationCheck | None = None timeout_ms: int | None = None retries: int | None = None + workdir: str | None = None def to_dict(self) -> dict[str, Any]: result: dict[str, Any] = { @@ -190,6 +218,8 @@ def to_dict(self) -> dict[str, Any]: result["timeoutMs"] = self.timeout_ms if self.retries is not None: result["retries"] = self.retries + if self.workdir is not None: + result["workdir"] = self.workdir return result @@ -285,6 +315,7 @@ class RelayYamlConfig: agents: list[AgentDefinition] version: str = "1.0" description: str | None = None + paths: list[PathDefinition] | None = None workflows: list[WorkflowDefinition] | None = None coordination: CoordinationConfig | None = None state: StateConfig | None = None @@ -300,6 +331,8 @@ def to_dict(self) -> dict[str, Any]: } if self.description is not None: result["description"] = self.description + if self.paths is not None: + result["paths"] = [p.to_dict() for p in self.paths] if self.workflows is not None: result["workflows"] = [workflow.to_dict() for workflow in self.workflows] if self.coordination is not None: diff --git a/packages/sdk/src/workflows/schema.json b/packages/sdk/src/workflows/schema.json index 2e11b8b1d..351b5d298 100644 --- a/packages/sdk/src/workflows/schema.json +++ b/packages/sdk/src/workflows/schema.json @@ -19,6 +19,13 @@ "type": "string", "description": "Optional description of the configuration" }, + "paths": { + "type": "array", + "description": "Named paths to external directories. The primary working directory defaults to cwd. Use this to declare additional directories so the runner can validate them in preflight and agents can reference them via workdir.", + "items": { + "$ref": "#/definitions/PathDefinition" + } + }, "swarm": { "$ref": "#/definitions/SwarmConfig" }, @@ -57,6 +64,30 @@ } }, "definitions": { + "PathDefinition": { + "type": "object", + "required": ["name", "path"], + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "description": "Unique name used to reference this project (e.g. 'relaycast', 'relay')" + }, + "path": { + "type": "string", + "description": "Path to the project root, resolved relative to the YAML file. Supports env vars: $HOME/.openclaw" + }, + "description": { + "type": "string", + "description": "Human-readable description of this project's role in the workflow" + }, + "required": { + "type": "boolean", + "default": true, + "description": "Whether this project is required. If true (default), preflight fails if path doesn't exist." + } + } + }, "SwarmConfig": { "type": "object", "required": ["pattern"], @@ -167,6 +198,19 @@ "default": true, "description": "When false, the agent runs as a non-interactive subprocess (no PTY, no relay messaging). It receives its task as a CLI prompt argument and returns stdout as output. Default: true." }, + "cwd": { + "type": "string", + "description": "Working directory for this agent, resolved relative to the YAML file." + }, + "workdir": { + "type": "string", + "description": "Sets this agent's working directory to a named entry from the top-level paths array. Mutually exclusive with cwd." + }, + "additionalPaths": { + "type": "array", + "items": { "type": "string" }, + "description": "Additional paths the agent needs read/write access to." + }, "preset": { "type": "string", "enum": ["lead", "worker", "reviewer", "analyst"], diff --git a/packages/sdk/src/workflows/types.ts b/packages/sdk/src/workflows/types.ts index cc129a6fc..1e6375b61 100644 --- a/packages/sdk/src/workflows/types.ts +++ b/packages/sdk/src/workflows/types.ts @@ -12,6 +12,11 @@ export interface RelayYamlConfig { version: string; name: string; description?: string; + /** Named paths to external directories used by this workflow. + * The primary working directory defaults to cwd and does not need to be declared. + * Use this to declare additional directories so the runner can validate them + * in preflight and agents can reference them via `workdir`. */ + paths?: PathDefinition[]; swarm: SwarmConfig; agents: AgentDefinition[]; workflows?: WorkflowDefinition[]; @@ -21,6 +26,22 @@ export interface RelayYamlConfig { trajectories?: TrajectoryConfig | false; } +// ── Path definitions ──────────────────────────────────────────────────────── + +/** A named path to an external directory for cross-repo workflows. + * Only needed for directories outside the default working directory. */ +export interface PathDefinition { + /** Unique name used to reference this path (e.g. "relaycast"). */ + name: string; + /** Directory path, resolved relative to the YAML file. + * Supports environment variables: "$HOME/.openclaw", "$RELAY_ROOT/packages/sdk". */ + path: string; + /** Human-readable description of this path's role in the workflow. */ + description?: string; + /** Whether this path is required. If true (default), preflight fails if it doesn't exist. */ + required?: boolean; +} + // ── Trajectory configuration ───────────────────────────────────────────────── /** Configuration for workflow trajectory recording. */ @@ -102,6 +123,9 @@ export interface AgentDefinition { interactive?: boolean; /** Working directory for this agent, resolved relative to the YAML file. */ cwd?: string; + /** Sets this agent's working directory to a named entry from the top-level `paths` array. + * Mutually exclusive with `cwd`. */ + workdir?: string; /** Additional paths the agent needs read/write access to. */ additionalPaths?: string[]; /** @@ -234,6 +258,8 @@ export interface WorkflowStep { // ── Deterministic step fields ────────────────────────────────────────────── /** Shell command to execute (required for deterministic steps). */ command?: string; + /** Sets this step's working directory to a named entry from the top-level `paths` array. */ + workdir?: string; /** Fail if command exit code is non-zero. Default: true. */ failOnError?: boolean; /** Capture stdout as step output for downstream steps. Default: true. */ From 1869034563e0507e1134be7a2b32275152cc947d Mon Sep 17 00:00:00 2001 From: Khaliq Date: Tue, 10 Mar 2026 07:16:29 +0100 Subject: [PATCH 2/6] feat: implement runtime support for paths and workdir in workflow runner MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The types and schema for `paths` (top-level) and `workdir` (agents/steps) were added in 75337dd2 but had no runtime implementation. This adds: - Path resolution with env var expansion ($HOME, $VAR) - Preflight validation in dryRun() (required/optional, duplicate names) - workdir→cwd mapping for agent steps (both PTY and non-interactive) - workdir→cwd mapping for deterministic steps - Step-level workdir override for agent steps - Includes real E2E test workflow for cross-repo validation Co-Authored-By: Claude Opus 4.6 --- packages/sdk/src/workflows/runner.ts | 152 ++++++++++++++++++-- tests/workflows/real-path-workdir-test.yaml | 42 ++++++ 2 files changed, 186 insertions(+), 8 deletions(-) create mode 100644 tests/workflows/real-path-workdir-test.yaml diff --git a/packages/sdk/src/workflows/runner.ts b/packages/sdk/src/workflows/runner.ts index 7bdbb8014..f6362259c 100644 --- a/packages/sdk/src/workflows/runner.ts +++ b/packages/sdk/src/workflows/runner.ts @@ -30,6 +30,7 @@ import type { DryRunWave, ErrorHandlingConfig, IdleNudgeConfig, + PathDefinition, PreflightCheck, RelayYamlConfig, SwarmPattern, @@ -202,6 +203,8 @@ export class WorkflowRunner { private readonly lastIdleLog = new Map(); /** Tracks last logged activity type per agent to avoid duplicate status lines. */ private readonly lastActivity = new Map(); + /** Resolved named paths from the top-level `paths` config, keyed by name → absolute directory. */ + private resolvedPaths = new Map(); constructor(options: WorkflowRunnerOptions = {}) { this.db = options.db ?? new InMemoryWorkflowDb(); @@ -213,6 +216,89 @@ export class WorkflowRunner { this.executor = options.executor; } + // ── Path resolution ───────────────────────────────────────────────────── + + /** Expand environment variables like $HOME or $VAR in a path string. */ + private static resolveEnvVars(p: string): string { + return p.replace(/\$([A-Za-z_][A-Za-z0-9_]*)/g, (_match, varName: string) => { + return process.env[varName] ?? _match; + }); + } + + /** + * Resolve and validate the top-level `paths` definitions from the config. + * Returns a map of name → absolute directory path. + * Throws if a required path does not exist. + */ + private resolvePathDefinitions( + pathDefs: PathDefinition[] | undefined, + baseCwd: string + ): { resolved: Map; errors: string[]; warnings: string[] } { + const resolved = new Map(); + const errors: string[] = []; + const warnings: string[] = []; + if (!pathDefs || pathDefs.length === 0) return { resolved, errors, warnings }; + + const seenNames = new Set(); + for (const pd of pathDefs) { + if (seenNames.has(pd.name)) { + errors.push(`Duplicate path name "${pd.name}"`); + continue; + } + seenNames.add(pd.name); + + const expanded = WorkflowRunner.resolveEnvVars(pd.path); + const abs = path.resolve(baseCwd, expanded); + resolved.set(pd.name, abs); + + const isRequired = pd.required !== false; // default true + if (!existsSync(abs)) { + if (isRequired) { + errors.push(`Path "${pd.name}" resolves to "${abs}" which does not exist (required)`); + } else { + warnings.push(`Path "${pd.name}" resolves to "${abs}" which does not exist (optional)`); + } + } + } + + return { resolved, errors, warnings }; + } + + /** + * Resolve an agent's effective working directory, considering `workdir` (named path reference) + * and `cwd` (explicit path). `workdir` takes precedence when both are set. + */ + private resolveAgentCwd(agent: AgentDefinition): string { + if (agent.workdir) { + const resolved = this.resolvedPaths.get(agent.workdir); + if (!resolved) { + throw new Error( + `Agent "${agent.name}" references workdir "${agent.workdir}" which is not defined in paths` + ); + } + return resolved; + } + if (agent.cwd) { + return path.resolve(this.cwd, agent.cwd); + } + return this.cwd; + } + + /** + * Resolve a step's working directory from its `workdir` field (named path reference). + * Returns undefined if no workdir is set. + */ + private resolveStepWorkdir(step: WorkflowStep): string | undefined { + if (!step.workdir) return undefined; + const resolved = this.resolvedPaths.get(step.workdir); + if (!resolved) { + throw new Error( + `Step "${step.name}" references workdir "${step.workdir}" which is not defined in paths` + ); + } + return resolved; + } + // ── Progress logging ──────────────────────────────────────────────────── /** Log a progress message with elapsed time since run start. */ @@ -494,6 +580,21 @@ export class WorkflowRunner { }; } + // 1b. Resolve and validate named paths + const pathResult = this.resolvePathDefinitions(resolved.paths, this.cwd); + errors.push(...pathResult.errors); + warnings.push(...pathResult.warnings); + const dryRunPaths = pathResult.resolved; + + // Validate workdir references on agents + for (const agent of resolved.agents) { + if (agent.workdir && !dryRunPaths.has(agent.workdir)) { + errors.push( + `Agent "${agent.name}" references workdir "${agent.workdir}" which is not defined in paths` + ); + } + } + // 2. Find target workflow const workflows = resolved.workflows ?? []; const workflow = workflowName ? workflows.find((w) => w.name === workflowName) : workflows[0]; @@ -564,6 +665,15 @@ export class WorkflowRunner { } } + // Validate workdir references on steps + for (const step of resolvedSteps) { + if (step.workdir && !dryRunPaths.has(step.workdir)) { + errors.push( + `Step "${step.name}" references workdir "${step.workdir}" which is not defined in paths` + ); + } + } + // Validate cwd paths for (const agent of resolved.agents) { if (agent.cwd) { @@ -666,7 +776,7 @@ export class WorkflowRunner { name: a.name, cli: a.cli, role: a.role, - cwd: a.cwd, + cwd: a.workdir ? dryRunPaths.get(a.workdir) : a.cwd, stepCount: stepAgentCounts.get(a.name) ?? 0, })); @@ -990,6 +1100,19 @@ export class WorkflowRunner { vars?: VariableContext ): Promise { const resolved = vars ? this.resolveVariables(config, vars) : config; + + // Resolve and validate named paths from the top-level `paths` config + const pathResult = this.resolvePathDefinitions(resolved.paths, this.cwd); + if (pathResult.errors.length > 0) { + throw new Error(`Path validation failed:\n ${pathResult.errors.join('\n ')}`); + } + this.resolvedPaths = pathResult.resolved; + if (this.resolvedPaths.size > 0) { + for (const [name, abs] of this.resolvedPaths) { + console.log(`[workflow] path "${name}" → ${abs}`); + } + } + const workflows = resolved.workflows ?? []; const workflow = workflowName ? workflows.find((w) => w.name === workflowName) : workflows[0]; @@ -1730,10 +1853,13 @@ export class WorkflowRunner { return value !== undefined ? String(value) : _match; }); + // Resolve step workdir (named path reference) for deterministic steps + const stepCwd = this.resolveStepWorkdir(step) ?? this.cwd; + try { // Delegate to executor if present if (this.executor?.executeDeterministicStep) { - const result = await this.executor.executeDeterministicStep(step, resolvedCommand, this.cwd); + const result = await this.executor.executeDeterministicStep(step, resolvedCommand, stepCwd); const failOnError = step.failOnError !== false; if (failOnError && result.exitCode !== 0) { throw new Error(`Command failed with exit code ${result.exitCode}: ${result.output.slice(0, 500)}`); @@ -1758,7 +1884,7 @@ export class WorkflowRunner { const output = await new Promise((resolve, reject) => { const child = cpSpawn('sh', ['-c', resolvedCommand], { stdio: 'pipe', - cwd: this.cwd, + cwd: stepCwd, env: { ...process.env }, }); @@ -2119,12 +2245,21 @@ export class WorkflowRunner { } } + // Apply step-level workdir override to agent definition if present + let effectiveAgentDef = agentDef; + if (step.workdir) { + const stepWorkdir = this.resolveStepWorkdir(step); + if (stepWorkdir) { + effectiveAgentDef = { ...agentDef, cwd: stepWorkdir, workdir: undefined }; + } + } + // Spawn agent via AgentRelay - this.log(`[${step.name}] Spawning agent "${agentDef.name}" (cli: ${agentDef.cli})`); + this.log(`[${step.name}] Spawning agent "${effectiveAgentDef.name}" (cli: ${effectiveAgentDef.cli})${step.workdir ? ` [workdir: ${step.workdir}]` : ''}`); const resolvedStep = { ...step, task: resolvedTask }; const output = this.executor - ? await this.executor.executeAgentStep(resolvedStep, agentDef, resolvedTask, timeoutMs) - : await this.spawnAndWait(agentDef, resolvedStep, timeoutMs); + ? await this.executor.executeAgentStep(resolvedStep, effectiveAgentDef, resolvedTask, timeoutMs) + : await this.spawnAndWait(effectiveAgentDef, resolvedStep, timeoutMs); this.log(`[${step.name}] Agent "${agentDef.name}" exited`); // Run verification if configured @@ -2333,7 +2468,7 @@ export class WorkflowRunner { const output = await new Promise((resolve, reject) => { const child = cpSpawn(cmd, args, { stdio: ['ignore', 'pipe', 'pipe'], - cwd: agentDef.cwd ? path.resolve(this.cwd, agentDef.cwd) : this.cwd, + cwd: this.resolveAgentCwd(agentDef), env: this.getRelayEnv() ?? { ...process.env }, }); @@ -2512,6 +2647,7 @@ export class WorkflowRunner { let ptyChunks: string[] = []; try { + const agentCwd = this.resolveAgentCwd(agentDef); agent = await this.relay.spawnPty({ name: agentName, cli: agentDef.cli, @@ -2520,7 +2656,7 @@ export class WorkflowRunner { channels: agentChannels, task: taskWithExit, idleThresholdSecs: agentDef.constraints?.idleThresholdSecs, - cwd: agentDef.cwd ? path.resolve(this.cwd, agentDef.cwd) : undefined, + cwd: agentCwd !== this.cwd ? agentCwd : undefined, }); // Re-key PTY maps if broker assigned a different name than requested diff --git a/tests/workflows/real-path-workdir-test.yaml b/tests/workflows/real-path-workdir-test.yaml new file mode 100644 index 000000000..949848507 --- /dev/null +++ b/tests/workflows/real-path-workdir-test.yaml @@ -0,0 +1,42 @@ +version: "1.0" +name: cross-repo-path-test +description: E2E test for paths and workdir features (PR #488) +paths: + - name: relaycast + path: /Users/khaliqgant/Projects/relaycast + description: Relaycast SDK repo + - name: relay-dashboard + path: /Users/khaliqgant/Projects/relay-dashboard + description: Dashboard UI +swarm: + pattern: dag + maxConcurrency: 2 + channel: path-test +agents: + - name: analyzer + cli: claude + role: Code analyzer + preset: worker +workflows: + - name: cross-repo-analysis + steps: + - name: check-relaycast + agent: analyzer + workdir: relaycast + task: | + Run `pwd` and `ls` in the current directory. Report what project this is based on the files you see. + At the very end of your response, output the exact string: RELAY_SDK_VERIFIED + verification: + type: output_contains + value: Relaycast + - name: check-dashboard + agent: analyzer + workdir: relay-dashboard + dependsOn: [check-relaycast] + task: | + Run `pwd` and `ls` in the current directory. Report what project this is. + Here is context from a previous step: {{steps.check-relaycast.output}} + At the very end of your response, output the exact string: DASH_VERIFIED + verification: + type: output_contains + value: dashboard From bbcd583f6a4c39a29b1c480f12cabd14248f68af Mon Sep 17 00:00:00 2001 From: Khaliq Date: Tue, 10 Mar 2026 10:12:28 +0100 Subject: [PATCH 3/6] fix(workflows): add workdir to step schema defs and resolve paths on resume Fixes two Devin review findings: 1. workdir missing from AgentWorkflowStep, DeterministicWorkflowStep, WorktreeWorkflowStep in schema.json (additionalProperties: false would reject workdir on steps) 2. resume() never called resolvePathDefinitions(), so workdir lookups would fail on resumed workflow runs --- .../cd590290340c324223857873/check-home.md | 1 + .../check-dashboard.md | 7 + .../check-relaycast.md | 12 + .agent-relay/storage-status.txt | 6 + .agent-relay/team/workers.json | 3 + .agent-relay/workflow-runs.jsonl | 23 ++ .../traj_1773123201611_9e34005f.json | 59 +++++ .../traj_1773123224392_b3a22ec2.json | 89 +++++++ .../traj_1773123294482_756d93a6.json | 97 ++++++++ packages/sdk/src/workflows/runner.ts | 8 + packages/sdk/src/workflows/schema.json | 219 ++++++++++++++---- 11 files changed, 482 insertions(+), 42 deletions(-) create mode 100644 .agent-relay/step-outputs/cd590290340c324223857873/check-home.md create mode 100644 .agent-relay/step-outputs/ee39e6c435a567f189511078/check-dashboard.md create mode 100644 .agent-relay/step-outputs/ee39e6c435a567f189511078/check-relaycast.md create mode 100644 .agent-relay/storage-status.txt create mode 100644 .agent-relay/team/workers.json create mode 100644 .agent-relay/workflow-runs.jsonl create mode 100644 .trajectories/completed/traj_1773123201611_9e34005f.json create mode 100644 .trajectories/completed/traj_1773123224392_b3a22ec2.json create mode 100644 .trajectories/completed/traj_1773123294482_756d93a6.json diff --git a/.agent-relay/step-outputs/cd590290340c324223857873/check-home.md b/.agent-relay/step-outputs/cd590290340c324223857873/check-home.md new file mode 100644 index 000000000..4d3d9f30f --- /dev/null +++ b/.agent-relay/step-outputs/cd590290340c324223857873/check-home.md @@ -0,0 +1 @@ +/Users/khaliqgant diff --git a/.agent-relay/step-outputs/ee39e6c435a567f189511078/check-dashboard.md b/.agent-relay/step-outputs/ee39e6c435a567f189511078/check-dashboard.md new file mode 100644 index 000000000..8a7673e63 --- /dev/null +++ b/.agent-relay/step-outputs/ee39e6c435a567f189511078/check-dashboard.md @@ -0,0 +1,7 @@ + + +**Project Report** + +This is the **relay-dashboard** project, located at `/Users/khaliqgant/Projects/relay-dashboard`. + +DASH_VERIFIED diff --git a/.agent-relay/step-outputs/ee39e6c435a567f189511078/check-relaycast.md b/.agent-relay/step-outputs/ee39e6c435a567f189511078/check-relaycast.md new file mode 100644 index 000000000..1f3715419 --- /dev/null +++ b/.agent-relay/step-outputs/ee39e6c435a567f189511078/check-relaycast.md @@ -0,0 +1,12 @@ +**Project: Relaycast** + +This is the **Relaycast** project, located at `/Users/khaliqgant/Projects/relaycast`. Based on the files present, it is a TypeScript monorepo (indicated by `turbo.json`, `tsconfig.base.json`, and `packages/` directory) that appears to be: + +- **A Cloudflare Workers-based service** — `wrangler.toml` and `wrangler.observer-router.toml` indicate Cloudflare Workers deployment +- **A multi-package monorepo** — managed with Turborepo (`turbo.json`) and npm (`package-lock.json`) +- **An API service** — has an `openapi.yaml` spec +- **Uses Drizzle ORM** — `drizzle.config.ts` for database management +- **Has a website/docs** — `site/` directory +- **Publishable as an MCP server** — `smithery.yaml` and `prpm.json` suggest it's a Model Context Protocol (MCP) server package, likely for inter-agent communication/relay + +RELAY_SDK_VERIFIED diff --git a/.agent-relay/storage-status.txt b/.agent-relay/storage-status.txt new file mode 100644 index 000000000..284779087 --- /dev/null +++ b/.agent-relay/storage-status.txt @@ -0,0 +1,6 @@ +status: ok +driver: better-sqlite3 +detail: better-sqlite3 rebuilt successfully +node: v22.22.1 +platform: darwin-arm64 +timestamp: 2026-03-10T06:05:59.562Z diff --git a/.agent-relay/team/workers.json b/.agent-relay/team/workers.json new file mode 100644 index 000000000..13b71e9a0 --- /dev/null +++ b/.agent-relay/team/workers.json @@ -0,0 +1,3 @@ +{ + "workers": [] +} \ No newline at end of file diff --git a/.agent-relay/workflow-runs.jsonl b/.agent-relay/workflow-runs.jsonl new file mode 100644 index 000000000..241fbebf6 --- /dev/null +++ b/.agent-relay/workflow-runs.jsonl @@ -0,0 +1,23 @@ +{"kind":"run","row":{"id":"cd590290340c324223857873","workspaceId":"local","workflowName":"test","pattern":"dag","status":"pending","config":{"version":"1.0","name":"env-var-path-test","paths":[{"name":"home-dir","path":"$HOME","description":"Home directory via env var"}],"swarm":{"pattern":"dag","maxConcurrency":1,"channel":"env-test"},"agents":[{"name":"worker","cli":"claude","role":"Worker"}],"workflows":[{"name":"test","steps":[{"name":"check-home","type":"deterministic","workdir":"home-dir","command":"pwd"}]}]},"startedAt":"2026-03-10T06:13:21.610Z","createdAt":"2026-03-10T06:13:21.610Z","updatedAt":"2026-03-10T06:13:21.610Z"}} +{"kind":"step","row":{"id":"5fdb057aa3ea949f39b738d6","runId":"cd590290340c324223857873","stepName":"check-home","agentName":null,"stepType":"deterministic","status":"pending","task":"pwd","dependsOn":[],"retryCount":0,"createdAt":"2026-03-10T06:13:21.610Z","updatedAt":"2026-03-10T06:13:21.610Z"}} +{"kind":"run","row":{"id":"cd590290340c324223857873","workspaceId":"local","workflowName":"test","pattern":"dag","status":"running","config":{"version":"1.0","name":"env-var-path-test","paths":[{"name":"home-dir","path":"$HOME","description":"Home directory via env var"}],"swarm":{"pattern":"dag","maxConcurrency":1,"channel":"env-test"},"agents":[{"name":"worker","cli":"claude","role":"Worker"}],"workflows":[{"name":"test","steps":[{"name":"check-home","type":"deterministic","workdir":"home-dir","command":"pwd"}]}]},"startedAt":"2026-03-10T06:13:21.610Z","createdAt":"2026-03-10T06:13:21.610Z","updatedAt":"2026-03-10T06:13:21.611Z"}} +{"kind":"step","row":{"id":"5fdb057aa3ea949f39b738d6","runId":"cd590290340c324223857873","stepName":"check-home","agentName":null,"stepType":"deterministic","status":"running","task":"pwd","dependsOn":[],"retryCount":0,"createdAt":"2026-03-10T06:13:21.610Z","updatedAt":"2026-03-10T06:13:25.154Z","startedAt":"2026-03-10T06:13:25.153Z"}} +{"kind":"step","row":{"id":"5fdb057aa3ea949f39b738d6","runId":"cd590290340c324223857873","stepName":"check-home","agentName":null,"stepType":"deterministic","status":"completed","task":"pwd","dependsOn":[],"retryCount":0,"createdAt":"2026-03-10T06:13:21.610Z","updatedAt":"2026-03-10T06:13:25.158Z","startedAt":"2026-03-10T06:13:25.153Z","output":"/Users/khaliqgant\n","completedAt":"2026-03-10T06:13:25.158Z"}} +{"kind":"run","row":{"id":"cd590290340c324223857873","workspaceId":"local","workflowName":"test","pattern":"dag","status":"completed","config":{"version":"1.0","name":"env-var-path-test","paths":[{"name":"home-dir","path":"$HOME","description":"Home directory via env var"}],"swarm":{"pattern":"dag","maxConcurrency":1,"channel":"env-test"},"agents":[{"name":"worker","cli":"claude","role":"Worker"}],"workflows":[{"name":"test","steps":[{"name":"check-home","type":"deterministic","workdir":"home-dir","command":"pwd"}]}]},"startedAt":"2026-03-10T06:13:21.610Z","createdAt":"2026-03-10T06:13:21.610Z","updatedAt":"2026-03-10T06:13:25.177Z","completedAt":"2026-03-10T06:13:25.177Z"}} +{"kind":"run","row":{"id":"fc23c441426d5520b8dbfe65","workspaceId":"local","workflowName":"cross-repo-analysis","pattern":"dag","status":"pending","config":{"version":"1.0","name":"cross-repo-path-test","description":"E2E test for paths and workdir features (PR","paths":[{"name":"relaycast","path":"/Users/khaliqgant/Projects/relaycast","description":"Relaycast SDK repo"},{"name":"relay-dashboard","path":"/Users/khaliqgant/Projects/relay-dashboard","description":"Dashboard UI"}],"swarm":{"pattern":"dag","maxConcurrency":2,"channel":"path-test"},"agents":[{"name":"analyzer","cli":"claude","role":"Code analyzer","preset":"worker"}],"workflows":[{"name":"cross-repo-analysis","steps":[{"name":"check-relaycast","agent":"analyzer","workdir":"relaycast","task":"List the top-level files in this directory and report what project this is.\nOutput: RELAYCAST_CHECKED\n","verification":{"type":"output_contains","value":"RELAYCAST_CHECKED"}},{"name":"check-dashboard","agent":"analyzer","workdir":"relay-dashboard","dependsOn":["check-relaycast"],"task":"List the top-level files and report what project this is.\nUse the output from the previous step for context: {{steps.check-relaycast.output}}\nOutput: DASHBOARD_CHECKED\n","verification":{"type":"output_contains","value":"DASHBOARD_CHECKED"}}]}]},"startedAt":"2026-03-10T06:13:44.390Z","createdAt":"2026-03-10T06:13:44.390Z","updatedAt":"2026-03-10T06:13:44.390Z"}} +{"kind":"step","row":{"id":"a58455d2a25e066529f34824","runId":"fc23c441426d5520b8dbfe65","stepName":"check-relaycast","agentName":"analyzer","stepType":"agent","status":"pending","task":"List the top-level files in this directory and report what project this is.\nOutput: RELAYCAST_CHECKED\n","dependsOn":[],"retryCount":0,"createdAt":"2026-03-10T06:13:44.390Z","updatedAt":"2026-03-10T06:13:44.390Z"}} +{"kind":"step","row":{"id":"4e0558fad3400f2c5f10657d","runId":"fc23c441426d5520b8dbfe65","stepName":"check-dashboard","agentName":"analyzer","stepType":"agent","status":"pending","task":"List the top-level files and report what project this is.\nUse the output from the previous step for context: {{steps.check-relaycast.output}}\nOutput: DASHBOARD_CHECKED\n","dependsOn":["check-relaycast"],"retryCount":0,"createdAt":"2026-03-10T06:13:44.390Z","updatedAt":"2026-03-10T06:13:44.390Z"}} +{"kind":"run","row":{"id":"fc23c441426d5520b8dbfe65","workspaceId":"local","workflowName":"cross-repo-analysis","pattern":"dag","status":"running","config":{"version":"1.0","name":"cross-repo-path-test","description":"E2E test for paths and workdir features (PR","paths":[{"name":"relaycast","path":"/Users/khaliqgant/Projects/relaycast","description":"Relaycast SDK repo"},{"name":"relay-dashboard","path":"/Users/khaliqgant/Projects/relay-dashboard","description":"Dashboard UI"}],"swarm":{"pattern":"dag","maxConcurrency":2,"channel":"path-test"},"agents":[{"name":"analyzer","cli":"claude","role":"Code analyzer","preset":"worker"}],"workflows":[{"name":"cross-repo-analysis","steps":[{"name":"check-relaycast","agent":"analyzer","workdir":"relaycast","task":"List the top-level files in this directory and report what project this is.\nOutput: RELAYCAST_CHECKED\n","verification":{"type":"output_contains","value":"RELAYCAST_CHECKED"}},{"name":"check-dashboard","agent":"analyzer","workdir":"relay-dashboard","dependsOn":["check-relaycast"],"task":"List the top-level files and report what project this is.\nUse the output from the previous step for context: {{steps.check-relaycast.output}}\nOutput: DASHBOARD_CHECKED\n","verification":{"type":"output_contains","value":"DASHBOARD_CHECKED"}}]}]},"startedAt":"2026-03-10T06:13:44.390Z","createdAt":"2026-03-10T06:13:44.390Z","updatedAt":"2026-03-10T06:13:44.391Z"}} +{"kind":"step","row":{"id":"a58455d2a25e066529f34824","runId":"fc23c441426d5520b8dbfe65","stepName":"check-relaycast","agentName":"analyzer","stepType":"agent","status":"running","task":"List the top-level files in this directory and report what project this is.\nOutput: RELAYCAST_CHECKED\n","dependsOn":[],"retryCount":0,"createdAt":"2026-03-10T06:13:44.390Z","updatedAt":"2026-03-10T06:13:47.954Z","startedAt":"2026-03-10T06:13:47.954Z"}} +{"kind":"step","row":{"id":"a58455d2a25e066529f34824","runId":"fc23c441426d5520b8dbfe65","stepName":"check-relaycast","agentName":"analyzer","stepType":"agent","status":"failed","task":"List the top-level files in this directory and report what project this is.\nOutput: RELAYCAST_CHECKED\n","dependsOn":[],"retryCount":0,"createdAt":"2026-03-10T06:13:44.390Z","updatedAt":"2026-03-10T06:14:00.141Z","startedAt":"2026-03-10T06:13:47.954Z","error":"Verification failed for \"check-relaycast\": output does not contain \"RELAYCAST_CHECKED\" (token found only in task injection — agent must output it explicitly)","completedAt":"2026-03-10T06:14:00.141Z"}} +{"kind":"step","row":{"id":"4e0558fad3400f2c5f10657d","runId":"fc23c441426d5520b8dbfe65","stepName":"check-dashboard","agentName":"analyzer","stepType":"agent","status":"skipped","task":"List the top-level files and report what project this is.\nUse the output from the previous step for context: {{steps.check-relaycast.output}}\nOutput: DASHBOARD_CHECKED\n","dependsOn":["check-relaycast"],"retryCount":0,"createdAt":"2026-03-10T06:13:44.390Z","updatedAt":"2026-03-10T06:14:00.142Z"}} +{"kind":"run","row":{"id":"fc23c441426d5520b8dbfe65","workspaceId":"local","workflowName":"cross-repo-analysis","pattern":"dag","status":"failed","config":{"version":"1.0","name":"cross-repo-path-test","description":"E2E test for paths and workdir features (PR","paths":[{"name":"relaycast","path":"/Users/khaliqgant/Projects/relaycast","description":"Relaycast SDK repo"},{"name":"relay-dashboard","path":"/Users/khaliqgant/Projects/relay-dashboard","description":"Dashboard UI"}],"swarm":{"pattern":"dag","maxConcurrency":2,"channel":"path-test"},"agents":[{"name":"analyzer","cli":"claude","role":"Code analyzer","preset":"worker"}],"workflows":[{"name":"cross-repo-analysis","steps":[{"name":"check-relaycast","agent":"analyzer","workdir":"relaycast","task":"List the top-level files in this directory and report what project this is.\nOutput: RELAYCAST_CHECKED\n","verification":{"type":"output_contains","value":"RELAYCAST_CHECKED"}},{"name":"check-dashboard","agent":"analyzer","workdir":"relay-dashboard","dependsOn":["check-relaycast"],"task":"List the top-level files and report what project this is.\nUse the output from the previous step for context: {{steps.check-relaycast.output}}\nOutput: DASHBOARD_CHECKED\n","verification":{"type":"output_contains","value":"DASHBOARD_CHECKED"}}]}]},"startedAt":"2026-03-10T06:13:44.390Z","createdAt":"2026-03-10T06:13:44.390Z","updatedAt":"2026-03-10T06:14:00.143Z","completedAt":"2026-03-10T06:14:00.143Z","error":"Step \"check-relaycast\" failed: Step \"check-relaycast\" failed after 0 retries: Verification failed for \"check-relaycast\": output does not contain \"RELAYCAST_CHECKED\" (token found only in task injection — agent must output it explicitly)"}} +{"kind":"run","row":{"id":"ee39e6c435a567f189511078","workspaceId":"local","workflowName":"cross-repo-analysis","pattern":"dag","status":"pending","config":{"version":"1.0","name":"cross-repo-path-test","description":"E2E test for paths and workdir features (PR","paths":[{"name":"relaycast","path":"/Users/khaliqgant/Projects/relaycast","description":"Relaycast SDK repo"},{"name":"relay-dashboard","path":"/Users/khaliqgant/Projects/relay-dashboard","description":"Dashboard UI"}],"swarm":{"pattern":"dag","maxConcurrency":2,"channel":"path-test"},"agents":[{"name":"analyzer","cli":"claude","role":"Code analyzer","preset":"worker"}],"workflows":[{"name":"cross-repo-analysis","steps":[{"name":"check-relaycast","agent":"analyzer","workdir":"relaycast","task":"Run `pwd` and `ls` in the current directory. Report what project this is based on the files you see.\nAt the very end of your response, output the exact string: RELAY_SDK_VERIFIED\n","verification":{"type":"output_contains","value":"Relaycast"}},{"name":"check-dashboard","agent":"analyzer","workdir":"relay-dashboard","dependsOn":["check-relaycast"],"task":"Run `pwd` and `ls` in the current directory. Report what project this is.\nHere is context from a previous step: {{steps.check-relaycast.output}}\nAt the very end of your response, output the exact string: DASH_VERIFIED\n","verification":{"type":"output_contains","value":"dashboard"}}]}]},"startedAt":"2026-03-10T06:14:54.481Z","createdAt":"2026-03-10T06:14:54.481Z","updatedAt":"2026-03-10T06:14:54.481Z"}} +{"kind":"step","row":{"id":"69388e7f4987eb66fbb43914","runId":"ee39e6c435a567f189511078","stepName":"check-relaycast","agentName":"analyzer","stepType":"agent","status":"pending","task":"Run `pwd` and `ls` in the current directory. Report what project this is based on the files you see.\nAt the very end of your response, output the exact string: RELAY_SDK_VERIFIED\n","dependsOn":[],"retryCount":0,"createdAt":"2026-03-10T06:14:54.481Z","updatedAt":"2026-03-10T06:14:54.481Z"}} +{"kind":"step","row":{"id":"f301df3739ac624e5b250a5d","runId":"ee39e6c435a567f189511078","stepName":"check-dashboard","agentName":"analyzer","stepType":"agent","status":"pending","task":"Run `pwd` and `ls` in the current directory. Report what project this is.\nHere is context from a previous step: {{steps.check-relaycast.output}}\nAt the very end of your response, output the exact string: DASH_VERIFIED\n","dependsOn":["check-relaycast"],"retryCount":0,"createdAt":"2026-03-10T06:14:54.481Z","updatedAt":"2026-03-10T06:14:54.481Z"}} +{"kind":"run","row":{"id":"ee39e6c435a567f189511078","workspaceId":"local","workflowName":"cross-repo-analysis","pattern":"dag","status":"running","config":{"version":"1.0","name":"cross-repo-path-test","description":"E2E test for paths and workdir features (PR","paths":[{"name":"relaycast","path":"/Users/khaliqgant/Projects/relaycast","description":"Relaycast SDK repo"},{"name":"relay-dashboard","path":"/Users/khaliqgant/Projects/relay-dashboard","description":"Dashboard UI"}],"swarm":{"pattern":"dag","maxConcurrency":2,"channel":"path-test"},"agents":[{"name":"analyzer","cli":"claude","role":"Code analyzer","preset":"worker"}],"workflows":[{"name":"cross-repo-analysis","steps":[{"name":"check-relaycast","agent":"analyzer","workdir":"relaycast","task":"Run `pwd` and `ls` in the current directory. Report what project this is based on the files you see.\nAt the very end of your response, output the exact string: RELAY_SDK_VERIFIED\n","verification":{"type":"output_contains","value":"Relaycast"}},{"name":"check-dashboard","agent":"analyzer","workdir":"relay-dashboard","dependsOn":["check-relaycast"],"task":"Run `pwd` and `ls` in the current directory. Report what project this is.\nHere is context from a previous step: {{steps.check-relaycast.output}}\nAt the very end of your response, output the exact string: DASH_VERIFIED\n","verification":{"type":"output_contains","value":"dashboard"}}]}]},"startedAt":"2026-03-10T06:14:54.481Z","createdAt":"2026-03-10T06:14:54.481Z","updatedAt":"2026-03-10T06:14:54.482Z"}} +{"kind":"step","row":{"id":"69388e7f4987eb66fbb43914","runId":"ee39e6c435a567f189511078","stepName":"check-relaycast","agentName":"analyzer","stepType":"agent","status":"running","task":"Run `pwd` and `ls` in the current directory. Report what project this is based on the files you see.\nAt the very end of your response, output the exact string: RELAY_SDK_VERIFIED\n","dependsOn":[],"retryCount":0,"createdAt":"2026-03-10T06:14:54.481Z","updatedAt":"2026-03-10T06:14:58.034Z","startedAt":"2026-03-10T06:14:58.033Z"}} +{"kind":"step","row":{"id":"69388e7f4987eb66fbb43914","runId":"ee39e6c435a567f189511078","stepName":"check-relaycast","agentName":"analyzer","stepType":"agent","status":"completed","task":"Run `pwd` and `ls` in the current directory. Report what project this is based on the files you see.\nAt the very end of your response, output the exact string: RELAY_SDK_VERIFIED\n","dependsOn":[],"retryCount":0,"createdAt":"2026-03-10T06:14:54.481Z","updatedAt":"2026-03-10T06:15:12.833Z","startedAt":"2026-03-10T06:14:58.033Z","output":"**Project: Relaycast**\n\nThis is the **Relaycast** project, located at `/Users/khaliqgant/Projects/relaycast`. Based on the files present, it is a TypeScript monorepo (indicated by `turbo.json`, `tsconfig.base.json`, and `packages/` directory) that appears to be:\n\n- **A Cloudflare Workers-based service** — `wrangler.toml` and `wrangler.observer-router.toml` indicate Cloudflare Workers deployment\n- **A multi-package monorepo** — managed with Turborepo (`turbo.json`) and npm (`package-lock.json`)\n- **An API service** — has an `openapi.yaml` spec\n- **Uses Drizzle ORM** — `drizzle.config.ts` for database management\n- **Has a website/docs** — `site/` directory\n- **Publishable as an MCP server** — `smithery.yaml` and `prpm.json` suggest it's a Model Context Protocol (MCP) server package, likely for inter-agent communication/relay\n\nRELAY_SDK_VERIFIED\n","completedAt":"2026-03-10T06:15:12.833Z"}} +{"kind":"step","row":{"id":"f301df3739ac624e5b250a5d","runId":"ee39e6c435a567f189511078","stepName":"check-dashboard","agentName":"analyzer","stepType":"agent","status":"running","task":"Run `pwd` and `ls` in the current directory. Report what project this is.\nHere is context from a previous step: {{steps.check-relaycast.output}}\nAt the very end of your response, output the exact string: DASH_VERIFIED\n","dependsOn":["check-relaycast"],"retryCount":0,"createdAt":"2026-03-10T06:14:54.481Z","updatedAt":"2026-03-10T06:15:12.835Z","startedAt":"2026-03-10T06:15:12.835Z"}} +{"kind":"step","row":{"id":"f301df3739ac624e5b250a5d","runId":"ee39e6c435a567f189511078","stepName":"check-dashboard","agentName":"analyzer","stepType":"agent","status":"completed","task":"Run `pwd` and `ls` in the current directory. Report what project this is.\nHere is context from a previous step: {{steps.check-relaycast.output}}\nAt the very end of your response, output the exact string: DASH_VERIFIED\n","dependsOn":["check-relaycast"],"retryCount":0,"createdAt":"2026-03-10T06:14:54.481Z","updatedAt":"2026-03-10T06:15:17.521Z","startedAt":"2026-03-10T06:15:12.835Z","output":"\n\n**Project Report**\n\nThis is the **relay-dashboard** project, located at `/Users/khaliqgant/Projects/relay-dashboard`.\n\nDASH_VERIFIED\n","completedAt":"2026-03-10T06:15:17.521Z"}} +{"kind":"run","row":{"id":"ee39e6c435a567f189511078","workspaceId":"local","workflowName":"cross-repo-analysis","pattern":"dag","status":"completed","config":{"version":"1.0","name":"cross-repo-path-test","description":"E2E test for paths and workdir features (PR","paths":[{"name":"relaycast","path":"/Users/khaliqgant/Projects/relaycast","description":"Relaycast SDK repo"},{"name":"relay-dashboard","path":"/Users/khaliqgant/Projects/relay-dashboard","description":"Dashboard UI"}],"swarm":{"pattern":"dag","maxConcurrency":2,"channel":"path-test"},"agents":[{"name":"analyzer","cli":"claude","role":"Code analyzer","preset":"worker"}],"workflows":[{"name":"cross-repo-analysis","steps":[{"name":"check-relaycast","agent":"analyzer","workdir":"relaycast","task":"Run `pwd` and `ls` in the current directory. Report what project this is based on the files you see.\nAt the very end of your response, output the exact string: RELAY_SDK_VERIFIED\n","verification":{"type":"output_contains","value":"Relaycast"}},{"name":"check-dashboard","agent":"analyzer","workdir":"relay-dashboard","dependsOn":["check-relaycast"],"task":"Run `pwd` and `ls` in the current directory. Report what project this is.\nHere is context from a previous step: {{steps.check-relaycast.output}}\nAt the very end of your response, output the exact string: DASH_VERIFIED\n","verification":{"type":"output_contains","value":"dashboard"}}]}]},"startedAt":"2026-03-10T06:14:54.481Z","createdAt":"2026-03-10T06:14:54.481Z","updatedAt":"2026-03-10T06:15:17.523Z","completedAt":"2026-03-10T06:15:17.523Z"}} diff --git a/.trajectories/completed/traj_1773123201611_9e34005f.json b/.trajectories/completed/traj_1773123201611_9e34005f.json new file mode 100644 index 000000000..7301030d4 --- /dev/null +++ b/.trajectories/completed/traj_1773123201611_9e34005f.json @@ -0,0 +1,59 @@ +{ + "id": "traj_1773123201611_9e34005f", + "version": 1, + "task": { + "title": "test run #cd590290", + "source": { + "system": "workflow-runner", + "id": "cd590290340c324223857873" + } + }, + "status": "completed", + "startedAt": "2026-03-10T06:13:21.611Z", + "agents": [ + { + "name": "orchestrator", + "role": "workflow-runner", + "joinedAt": "2026-03-10T06:13:21.611Z" + } + ], + "chapters": [ + { + "id": "ch_2902721b", + "title": "Planning", + "agentName": "orchestrator", + "startedAt": "2026-03-10T06:13:21.611Z", + "events": [ + { + "ts": 1773123201611, + "type": "note", + "content": "Approach: 1-step dag workflow — Parsed 1 steps, DAG validated, no cycles" + } + ], + "endedAt": "2026-03-10T06:13:25.182Z" + }, + { + "id": "ch_3163e0ca", + "title": "Retrospective", + "agentName": "orchestrator", + "startedAt": "2026-03-10T06:13:25.182Z", + "events": [ + { + "ts": 1773123205182, + "type": "reflection", + "content": "All 1 steps completed in 4s. (completed in 4 seconds)", + "significance": "high" + } + ], + "endedAt": "2026-03-10T06:13:25.182Z" + } + ], + "completedAt": "2026-03-10T06:13:25.182Z", + "retrospective": { + "summary": "All 1 steps completed in 4s.", + "approach": "dag workflow (0 agents)", + "confidence": 0.75, + "learnings": [], + "challenges": [] + } +} \ No newline at end of file diff --git a/.trajectories/completed/traj_1773123224392_b3a22ec2.json b/.trajectories/completed/traj_1773123224392_b3a22ec2.json new file mode 100644 index 000000000..ab56d75df --- /dev/null +++ b/.trajectories/completed/traj_1773123224392_b3a22ec2.json @@ -0,0 +1,89 @@ +{ + "id": "traj_1773123224392_b3a22ec2", + "version": 1, + "task": { + "title": "cross-repo-analysis run #fc23c441", + "source": { + "system": "workflow-runner", + "id": "fc23c441426d5520b8dbfe65" + } + }, + "status": "abandoned", + "startedAt": "2026-03-10T06:13:44.392Z", + "agents": [ + { + "name": "orchestrator", + "role": "workflow-runner", + "joinedAt": "2026-03-10T06:13:44.392Z" + }, + { + "name": "analyzer", + "role": "analyzer", + "joinedAt": "2026-03-10T06:13:47.954Z" + } + ], + "chapters": [ + { + "id": "ch_8b144dd2", + "title": "Planning", + "agentName": "orchestrator", + "startedAt": "2026-03-10T06:13:44.392Z", + "events": [ + { + "ts": 1773123224392, + "type": "note", + "content": "Purpose: E2E test for paths and workdir features (PR" + }, + { + "ts": 1773123224392, + "type": "note", + "content": "Approach: 2-step dag workflow — Parsed 2 steps, 1 dependent steps, DAG validated, no cycles" + }, + { + "ts": 1773123227954, + "type": "note", + "content": "\"check-relaycast\": List the top-level files in this directory and report what project this is", + "raw": { + "agent": "analyzer" + } + }, + { + "ts": 1773123240141, + "type": "error", + "content": "\"check-relaycast\" failed [verification_mismatch]: Agent completed but did not output the expected sentinel \"RELAYCAST_CHECKED\". The task prompt may not clearly specify the required output format, or the agent produced correct work but did not emit the signal.", + "significance": "high", + "raw": { + "cause": "verification_mismatch", + "rawError": "Verification failed for \"check-relaycast\": output does not contain \"RELAYCAST_CHECKED\" (token found only in task injection — agent must output it explicitly)", + "attempt": 1, + "maxRetries": 0 + } + }, + { + "ts": 1773123240143, + "type": "note", + "content": "\"check-dashboard\" skipped — Upstream dependency \"check-relaycast\" failed" + }, + { + "ts": 1773123240143, + "type": "decision", + "content": "Whether to skip check-dashboard → skip: Upstream dependency \"check-relaycast\" failed", + "significance": "medium", + "raw": { + "question": "Whether to skip check-dashboard", + "chosen": "skip", + "reasoning": "Upstream dependency \"check-relaycast\" failed" + } + }, + { + "ts": 1773123240143, + "type": "error", + "content": "Workflow abandoned: Step \"check-relaycast\" failed: Step \"check-relaycast\" failed after 0 retries: Verification failed for \"check-relaycast\": output does not contain \"RELAYCAST_CHECKED\" (token found only in task injection — agent must output it explicitly)", + "significance": "high" + } + ], + "endedAt": "2026-03-10T06:14:00.143Z" + } + ], + "completedAt": "2026-03-10T06:14:00.143Z" +} \ No newline at end of file diff --git a/.trajectories/completed/traj_1773123294482_756d93a6.json b/.trajectories/completed/traj_1773123294482_756d93a6.json new file mode 100644 index 000000000..6e071fe71 --- /dev/null +++ b/.trajectories/completed/traj_1773123294482_756d93a6.json @@ -0,0 +1,97 @@ +{ + "id": "traj_1773123294482_756d93a6", + "version": 1, + "task": { + "title": "cross-repo-analysis run #ee39e6c4", + "source": { + "system": "workflow-runner", + "id": "ee39e6c435a567f189511078" + } + }, + "status": "completed", + "startedAt": "2026-03-10T06:14:54.482Z", + "agents": [ + { + "name": "orchestrator", + "role": "workflow-runner", + "joinedAt": "2026-03-10T06:14:54.482Z" + }, + { + "name": "analyzer", + "role": "analyzer", + "joinedAt": "2026-03-10T06:14:58.034Z" + } + ], + "chapters": [ + { + "id": "ch_ce1d459e", + "title": "Planning", + "agentName": "orchestrator", + "startedAt": "2026-03-10T06:14:54.482Z", + "events": [ + { + "ts": 1773123294482, + "type": "note", + "content": "Purpose: E2E test for paths and workdir features (PR" + }, + { + "ts": 1773123294482, + "type": "note", + "content": "Approach: 2-step dag workflow — Parsed 2 steps, 1 dependent steps, DAG validated, no cycles" + }, + { + "ts": 1773123298034, + "type": "note", + "content": "\"check-relaycast\": Run `pwd` and `ls` in the current directory", + "raw": { + "agent": "analyzer" + } + }, + { + "ts": 1773123312835, + "type": "finding", + "content": "\"check-relaycast\" completed → RELAY_SDK_VERIFIED", + "significance": "medium" + }, + { + "ts": 1773123312836, + "type": "note", + "content": "\"check-dashboard\": Run `pwd` and `ls` in the current directory", + "raw": { + "agent": "analyzer" + } + }, + { + "ts": 1773123317522, + "type": "finding", + "content": "\"check-dashboard\" completed → DASH_VERIFIED", + "significance": "medium" + } + ], + "endedAt": "2026-03-10T06:15:17.523Z" + }, + { + "id": "ch_9bd24134", + "title": "Retrospective", + "agentName": "orchestrator", + "startedAt": "2026-03-10T06:15:17.523Z", + "events": [ + { + "ts": 1773123317523, + "type": "reflection", + "content": "All 2 steps completed in 23s. (completed in 23 seconds)", + "significance": "high" + } + ], + "endedAt": "2026-03-10T06:15:17.523Z" + } + ], + "completedAt": "2026-03-10T06:15:17.523Z", + "retrospective": { + "summary": "All 2 steps completed in 23s.", + "approach": "dag workflow (1 agents)", + "confidence": 1, + "learnings": [], + "challenges": [] + } +} \ No newline at end of file diff --git a/packages/sdk/src/workflows/runner.ts b/packages/sdk/src/workflows/runner.ts index f6362259c..7adafac3f 100644 --- a/packages/sdk/src/workflows/runner.ts +++ b/packages/sdk/src/workflows/runner.ts @@ -1194,6 +1194,14 @@ export class WorkflowRunner { } const config = vars ? this.resolveVariables(run.config, vars) : run.config; + + // Resolve path definitions (same as execute()) so workdir lookups work on resume + const pathResult = this.resolvePathDefinitions(config.paths, this.cwd); + if (pathResult.errors.length > 0) { + throw new Error(`Path validation failed:\n ${pathResult.errors.join('\n ')}`); + } + this.resolvedPaths = pathResult.resolved; + const workflows = config.workflows ?? []; const workflow = workflows.find((w) => w.name === run.workflowName); if (!workflow) { diff --git a/packages/sdk/src/workflows/schema.json b/packages/sdk/src/workflows/schema.json index 351b5d298..1127ff429 100644 --- a/packages/sdk/src/workflows/schema.json +++ b/packages/sdk/src/workflows/schema.json @@ -1,10 +1,17 @@ +Added workdir to AgentWorkflowStep +Added workdir to DeterministicWorkflowStep +Added workdir to WorktreeWorkflowStep { "$schema": "http://json-schema.org/draft-07/schema#", "$id": "RelayYamlConfig", "title": "Relay YAML Configuration", "description": "Schema for relay.yaml workflow configuration files", "type": "object", - "required": ["version", "name", "swarm"], + "required": [ + "version", + "name", + "swarm" + ], "additionalProperties": false, "properties": { "version": { @@ -58,7 +65,9 @@ }, { "type": "boolean", - "enum": [false] + "enum": [ + false + ] } ] } @@ -66,7 +75,10 @@ "definitions": { "PathDefinition": { "type": "object", - "required": ["name", "path"], + "required": [ + "name", + "path" + ], "additionalProperties": false, "properties": { "name": { @@ -90,7 +102,9 @@ }, "SwarmConfig": { "type": "object", - "required": ["pattern"], + "required": [ + "pattern" + ], "additionalProperties": false, "properties": { "pattern": { @@ -167,7 +181,10 @@ }, "AgentDefinition": { "type": "object", - "required": ["name", "cli"], + "required": [ + "name", + "cli" + ], "additionalProperties": false, "properties": { "name": { @@ -187,7 +204,9 @@ }, "channels": { "type": "array", - "items": { "type": "string" }, + "items": { + "type": "string" + }, "description": "Relay channels the agent should join" }, "constraints": { @@ -208,19 +227,37 @@ }, "additionalPaths": { "type": "array", - "items": { "type": "string" }, + "items": { + "type": "string" + }, "description": "Additional paths the agent needs read/write access to." }, "preset": { "type": "string", - "enum": ["lead", "worker", "reviewer", "analyst"], + "enum": [ + "lead", + "worker", + "reviewer", + "analyst" + ], "description": "Role preset that automatically configures interactive mode and injects appropriate task guardrails. lead: interactive PTY, relay-aware. worker/reviewer/analyst: interactive: false, no sub-agents." } } }, "AgentCli": { "type": "string", - "enum": ["claude", "codex", "gemini", "aider", "goose", "opencode", "droid", "cursor", "cursor-agent", "agent"] + "enum": [ + "claude", + "codex", + "gemini", + "aider", + "goose", + "opencode", + "droid", + "cursor", + "cursor-agent", + "agent" + ] }, "AgentConstraints": { "type": "object", @@ -276,7 +313,10 @@ }, "WorkflowDefinition": { "type": "object", - "required": ["name", "steps"], + "required": [ + "name", + "steps" + ], "additionalProperties": false, "properties": { "name": { @@ -302,14 +342,20 @@ }, "onError": { "type": "string", - "enum": ["fail", "skip", "retry"], + "enum": [ + "fail", + "skip", + "retry" + ], "description": "Error handling strategy for this workflow" } } }, "PreflightCheck": { "type": "object", - "required": ["command"], + "required": [ + "command" + ], "additionalProperties": false, "properties": { "command": { @@ -332,20 +378,34 @@ }, "WorkflowStep": { "oneOf": [ - { "$ref": "#/definitions/AgentWorkflowStep" }, - { "$ref": "#/definitions/DeterministicWorkflowStep" }, - { "$ref": "#/definitions/WorktreeWorkflowStep" }, - { "$ref": "#/definitions/CustomWorkflowStep" } + { + "$ref": "#/definitions/AgentWorkflowStep" + }, + { + "$ref": "#/definitions/DeterministicWorkflowStep" + }, + { + "$ref": "#/definitions/WorktreeWorkflowStep" + }, + { + "$ref": "#/definitions/CustomWorkflowStep" + } ] }, "AgentWorkflowStep": { "type": "object", - "required": ["name", "agent", "task"], + "required": [ + "name", + "agent", + "task" + ], "additionalProperties": false, "properties": { "type": { "type": "string", - "enum": ["agent"], + "enum": [ + "agent" + ], "description": "Step type (optional for agent steps, defaults to 'agent')" }, "name": { @@ -362,7 +422,9 @@ }, "dependsOn": { "type": "array", - "items": { "type": "string" }, + "items": { + "type": "string" + }, "description": "Step names that must complete before this step runs" }, "verification": { @@ -380,17 +442,27 @@ "type": "integer", "minimum": 1, "description": "Maximum iterations for steps that may need to retry" + }, + "workdir": { + "type": "string", + "description": "Sets this step's working directory to a named entry from the top-level paths array." } } }, "DeterministicWorkflowStep": { "type": "object", - "required": ["name", "type", "command"], + "required": [ + "name", + "type", + "command" + ], "additionalProperties": false, "properties": { "type": { "type": "string", - "enum": ["deterministic"], + "enum": [ + "deterministic" + ], "description": "Step type - must be 'deterministic' for shell command steps" }, "name": { @@ -403,7 +475,9 @@ }, "dependsOn": { "type": "array", - "items": { "type": "string" }, + "items": { + "type": "string" + }, "description": "Step names that must complete before this step runs" }, "timeoutMs": { @@ -419,17 +493,27 @@ "type": "boolean", "default": true, "description": "Capture stdout as step output for downstream steps" + }, + "workdir": { + "type": "string", + "description": "Sets this step's working directory to a named entry from the top-level paths array." } } }, "WorktreeWorkflowStep": { "type": "object", - "required": ["name", "type", "branch"], + "required": [ + "name", + "type", + "branch" + ], "additionalProperties": false, "properties": { "type": { "type": "string", - "enum": ["worktree"], + "enum": [ + "worktree" + ], "description": "Step type - must be 'worktree' for git worktree setup steps" }, "name": { @@ -455,18 +539,27 @@ }, "dependsOn": { "type": "array", - "items": { "type": "string" }, + "items": { + "type": "string" + }, "description": "Step names that must complete before this step runs" }, "timeoutMs": { "type": "integer", "minimum": 0 + }, + "workdir": { + "type": "string", + "description": "Sets this step's working directory to a named entry from the top-level paths array." } } }, "CustomWorkflowStep": { "type": "object", - "required": ["name", "use"], + "required": [ + "name", + "use" + ], "additionalProperties": true, "properties": { "name": { @@ -479,7 +572,9 @@ }, "dependsOn": { "type": "array", - "items": { "type": "string" }, + "items": { + "type": "string" + }, "description": "Step names that must complete before this step runs" }, "timeoutMs": { @@ -490,7 +585,9 @@ }, "CustomStepParam": { "type": "object", - "required": ["name"], + "required": [ + "name" + ], "additionalProperties": false, "properties": { "name": { @@ -518,12 +615,17 @@ "properties": { "params": { "type": "array", - "items": { "$ref": "#/definitions/CustomStepParam" }, + "items": { + "$ref": "#/definitions/CustomStepParam" + }, "description": "Parameters that can be passed when using this step" }, "type": { "type": "string", - "enum": ["deterministic", "worktree"], + "enum": [ + "deterministic", + "worktree" + ], "description": "Step type" }, "command": { @@ -569,24 +671,36 @@ }, "CustomStepsConfig": { "type": "object", - "required": ["steps"], + "required": [ + "steps" + ], "additionalProperties": false, "properties": { "steps": { "type": "object", - "additionalProperties": { "$ref": "#/definitions/CustomStepDefinition" }, + "additionalProperties": { + "$ref": "#/definitions/CustomStepDefinition" + }, "description": "Map of step name to step definition" } } }, "VerificationCheck": { "type": "object", - "required": ["type", "value"], + "required": [ + "type", + "value" + ], "additionalProperties": false, "properties": { "type": { "type": "string", - "enum": ["output_contains", "exit_code", "file_exists", "custom"], + "enum": [ + "output_contains", + "exit_code", + "file_exists", + "custom" + ], "description": "Type of verification to perform" }, "value": { @@ -617,14 +731,21 @@ }, "consensusStrategy": { "type": "string", - "enum": ["majority", "unanimous", "quorum"], + "enum": [ + "majority", + "unanimous", + "quorum" + ], "description": "Strategy for reaching consensus among agents" } } }, "Barrier": { "type": "object", - "required": ["name", "waitFor"], + "required": [ + "name", + "waitFor" + ], "additionalProperties": false, "properties": { "name": { @@ -633,7 +754,9 @@ }, "waitFor": { "type": "array", - "items": { "type": "string" }, + "items": { + "type": "string" + }, "minItems": 1, "description": "Agent or step names to wait for before proceeding" }, @@ -646,12 +769,18 @@ }, "StateConfig": { "type": "object", - "required": ["backend"], + "required": [ + "backend" + ], "additionalProperties": false, "properties": { "backend": { "type": "string", - "enum": ["memory", "redis", "database"], + "enum": [ + "memory", + "redis", + "database" + ], "description": "State storage backend" }, "ttlMs": { @@ -667,12 +796,18 @@ }, "ErrorHandlingConfig": { "type": "object", - "required": ["strategy"], + "required": [ + "strategy" + ], "additionalProperties": false, "properties": { "strategy": { "type": "string", - "enum": ["fail-fast", "continue", "retry"], + "enum": [ + "fail-fast", + "continue", + "retry" + ], "description": "Global error handling strategy" }, "maxRetries": { @@ -692,4 +827,4 @@ } } } -} +} \ No newline at end of file From fe4de78dc03f11b497677c37826f7ab37f58f998 Mon Sep 17 00:00:00 2001 From: Khaliq Date: Tue, 10 Mar 2026 10:29:55 +0100 Subject: [PATCH 4/6] chore: remove .agent-relay artifacts and gitignore --- .../cd590290340c324223857873/check-home.md | 1 - .../check-dashboard.md | 7 ------ .../check-relaycast.md | 12 ---------- .agent-relay/storage-status.txt | 6 ----- .agent-relay/team/workers.json | 3 --- .agent-relay/workflow-runs.jsonl | 23 ------------------- .gitignore | 1 + 7 files changed, 1 insertion(+), 52 deletions(-) delete mode 100644 .agent-relay/step-outputs/cd590290340c324223857873/check-home.md delete mode 100644 .agent-relay/step-outputs/ee39e6c435a567f189511078/check-dashboard.md delete mode 100644 .agent-relay/step-outputs/ee39e6c435a567f189511078/check-relaycast.md delete mode 100644 .agent-relay/storage-status.txt delete mode 100644 .agent-relay/team/workers.json delete mode 100644 .agent-relay/workflow-runs.jsonl diff --git a/.agent-relay/step-outputs/cd590290340c324223857873/check-home.md b/.agent-relay/step-outputs/cd590290340c324223857873/check-home.md deleted file mode 100644 index 4d3d9f30f..000000000 --- a/.agent-relay/step-outputs/cd590290340c324223857873/check-home.md +++ /dev/null @@ -1 +0,0 @@ -/Users/khaliqgant diff --git a/.agent-relay/step-outputs/ee39e6c435a567f189511078/check-dashboard.md b/.agent-relay/step-outputs/ee39e6c435a567f189511078/check-dashboard.md deleted file mode 100644 index 8a7673e63..000000000 --- a/.agent-relay/step-outputs/ee39e6c435a567f189511078/check-dashboard.md +++ /dev/null @@ -1,7 +0,0 @@ - - -**Project Report** - -This is the **relay-dashboard** project, located at `/Users/khaliqgant/Projects/relay-dashboard`. - -DASH_VERIFIED diff --git a/.agent-relay/step-outputs/ee39e6c435a567f189511078/check-relaycast.md b/.agent-relay/step-outputs/ee39e6c435a567f189511078/check-relaycast.md deleted file mode 100644 index 1f3715419..000000000 --- a/.agent-relay/step-outputs/ee39e6c435a567f189511078/check-relaycast.md +++ /dev/null @@ -1,12 +0,0 @@ -**Project: Relaycast** - -This is the **Relaycast** project, located at `/Users/khaliqgant/Projects/relaycast`. Based on the files present, it is a TypeScript monorepo (indicated by `turbo.json`, `tsconfig.base.json`, and `packages/` directory) that appears to be: - -- **A Cloudflare Workers-based service** — `wrangler.toml` and `wrangler.observer-router.toml` indicate Cloudflare Workers deployment -- **A multi-package monorepo** — managed with Turborepo (`turbo.json`) and npm (`package-lock.json`) -- **An API service** — has an `openapi.yaml` spec -- **Uses Drizzle ORM** — `drizzle.config.ts` for database management -- **Has a website/docs** — `site/` directory -- **Publishable as an MCP server** — `smithery.yaml` and `prpm.json` suggest it's a Model Context Protocol (MCP) server package, likely for inter-agent communication/relay - -RELAY_SDK_VERIFIED diff --git a/.agent-relay/storage-status.txt b/.agent-relay/storage-status.txt deleted file mode 100644 index 284779087..000000000 --- a/.agent-relay/storage-status.txt +++ /dev/null @@ -1,6 +0,0 @@ -status: ok -driver: better-sqlite3 -detail: better-sqlite3 rebuilt successfully -node: v22.22.1 -platform: darwin-arm64 -timestamp: 2026-03-10T06:05:59.562Z diff --git a/.agent-relay/team/workers.json b/.agent-relay/team/workers.json deleted file mode 100644 index 13b71e9a0..000000000 --- a/.agent-relay/team/workers.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "workers": [] -} \ No newline at end of file diff --git a/.agent-relay/workflow-runs.jsonl b/.agent-relay/workflow-runs.jsonl deleted file mode 100644 index 241fbebf6..000000000 --- a/.agent-relay/workflow-runs.jsonl +++ /dev/null @@ -1,23 +0,0 @@ -{"kind":"run","row":{"id":"cd590290340c324223857873","workspaceId":"local","workflowName":"test","pattern":"dag","status":"pending","config":{"version":"1.0","name":"env-var-path-test","paths":[{"name":"home-dir","path":"$HOME","description":"Home directory via env var"}],"swarm":{"pattern":"dag","maxConcurrency":1,"channel":"env-test"},"agents":[{"name":"worker","cli":"claude","role":"Worker"}],"workflows":[{"name":"test","steps":[{"name":"check-home","type":"deterministic","workdir":"home-dir","command":"pwd"}]}]},"startedAt":"2026-03-10T06:13:21.610Z","createdAt":"2026-03-10T06:13:21.610Z","updatedAt":"2026-03-10T06:13:21.610Z"}} -{"kind":"step","row":{"id":"5fdb057aa3ea949f39b738d6","runId":"cd590290340c324223857873","stepName":"check-home","agentName":null,"stepType":"deterministic","status":"pending","task":"pwd","dependsOn":[],"retryCount":0,"createdAt":"2026-03-10T06:13:21.610Z","updatedAt":"2026-03-10T06:13:21.610Z"}} -{"kind":"run","row":{"id":"cd590290340c324223857873","workspaceId":"local","workflowName":"test","pattern":"dag","status":"running","config":{"version":"1.0","name":"env-var-path-test","paths":[{"name":"home-dir","path":"$HOME","description":"Home directory via env var"}],"swarm":{"pattern":"dag","maxConcurrency":1,"channel":"env-test"},"agents":[{"name":"worker","cli":"claude","role":"Worker"}],"workflows":[{"name":"test","steps":[{"name":"check-home","type":"deterministic","workdir":"home-dir","command":"pwd"}]}]},"startedAt":"2026-03-10T06:13:21.610Z","createdAt":"2026-03-10T06:13:21.610Z","updatedAt":"2026-03-10T06:13:21.611Z"}} -{"kind":"step","row":{"id":"5fdb057aa3ea949f39b738d6","runId":"cd590290340c324223857873","stepName":"check-home","agentName":null,"stepType":"deterministic","status":"running","task":"pwd","dependsOn":[],"retryCount":0,"createdAt":"2026-03-10T06:13:21.610Z","updatedAt":"2026-03-10T06:13:25.154Z","startedAt":"2026-03-10T06:13:25.153Z"}} -{"kind":"step","row":{"id":"5fdb057aa3ea949f39b738d6","runId":"cd590290340c324223857873","stepName":"check-home","agentName":null,"stepType":"deterministic","status":"completed","task":"pwd","dependsOn":[],"retryCount":0,"createdAt":"2026-03-10T06:13:21.610Z","updatedAt":"2026-03-10T06:13:25.158Z","startedAt":"2026-03-10T06:13:25.153Z","output":"/Users/khaliqgant\n","completedAt":"2026-03-10T06:13:25.158Z"}} -{"kind":"run","row":{"id":"cd590290340c324223857873","workspaceId":"local","workflowName":"test","pattern":"dag","status":"completed","config":{"version":"1.0","name":"env-var-path-test","paths":[{"name":"home-dir","path":"$HOME","description":"Home directory via env var"}],"swarm":{"pattern":"dag","maxConcurrency":1,"channel":"env-test"},"agents":[{"name":"worker","cli":"claude","role":"Worker"}],"workflows":[{"name":"test","steps":[{"name":"check-home","type":"deterministic","workdir":"home-dir","command":"pwd"}]}]},"startedAt":"2026-03-10T06:13:21.610Z","createdAt":"2026-03-10T06:13:21.610Z","updatedAt":"2026-03-10T06:13:25.177Z","completedAt":"2026-03-10T06:13:25.177Z"}} -{"kind":"run","row":{"id":"fc23c441426d5520b8dbfe65","workspaceId":"local","workflowName":"cross-repo-analysis","pattern":"dag","status":"pending","config":{"version":"1.0","name":"cross-repo-path-test","description":"E2E test for paths and workdir features (PR","paths":[{"name":"relaycast","path":"/Users/khaliqgant/Projects/relaycast","description":"Relaycast SDK repo"},{"name":"relay-dashboard","path":"/Users/khaliqgant/Projects/relay-dashboard","description":"Dashboard UI"}],"swarm":{"pattern":"dag","maxConcurrency":2,"channel":"path-test"},"agents":[{"name":"analyzer","cli":"claude","role":"Code analyzer","preset":"worker"}],"workflows":[{"name":"cross-repo-analysis","steps":[{"name":"check-relaycast","agent":"analyzer","workdir":"relaycast","task":"List the top-level files in this directory and report what project this is.\nOutput: RELAYCAST_CHECKED\n","verification":{"type":"output_contains","value":"RELAYCAST_CHECKED"}},{"name":"check-dashboard","agent":"analyzer","workdir":"relay-dashboard","dependsOn":["check-relaycast"],"task":"List the top-level files and report what project this is.\nUse the output from the previous step for context: {{steps.check-relaycast.output}}\nOutput: DASHBOARD_CHECKED\n","verification":{"type":"output_contains","value":"DASHBOARD_CHECKED"}}]}]},"startedAt":"2026-03-10T06:13:44.390Z","createdAt":"2026-03-10T06:13:44.390Z","updatedAt":"2026-03-10T06:13:44.390Z"}} -{"kind":"step","row":{"id":"a58455d2a25e066529f34824","runId":"fc23c441426d5520b8dbfe65","stepName":"check-relaycast","agentName":"analyzer","stepType":"agent","status":"pending","task":"List the top-level files in this directory and report what project this is.\nOutput: RELAYCAST_CHECKED\n","dependsOn":[],"retryCount":0,"createdAt":"2026-03-10T06:13:44.390Z","updatedAt":"2026-03-10T06:13:44.390Z"}} -{"kind":"step","row":{"id":"4e0558fad3400f2c5f10657d","runId":"fc23c441426d5520b8dbfe65","stepName":"check-dashboard","agentName":"analyzer","stepType":"agent","status":"pending","task":"List the top-level files and report what project this is.\nUse the output from the previous step for context: {{steps.check-relaycast.output}}\nOutput: DASHBOARD_CHECKED\n","dependsOn":["check-relaycast"],"retryCount":0,"createdAt":"2026-03-10T06:13:44.390Z","updatedAt":"2026-03-10T06:13:44.390Z"}} -{"kind":"run","row":{"id":"fc23c441426d5520b8dbfe65","workspaceId":"local","workflowName":"cross-repo-analysis","pattern":"dag","status":"running","config":{"version":"1.0","name":"cross-repo-path-test","description":"E2E test for paths and workdir features (PR","paths":[{"name":"relaycast","path":"/Users/khaliqgant/Projects/relaycast","description":"Relaycast SDK repo"},{"name":"relay-dashboard","path":"/Users/khaliqgant/Projects/relay-dashboard","description":"Dashboard UI"}],"swarm":{"pattern":"dag","maxConcurrency":2,"channel":"path-test"},"agents":[{"name":"analyzer","cli":"claude","role":"Code analyzer","preset":"worker"}],"workflows":[{"name":"cross-repo-analysis","steps":[{"name":"check-relaycast","agent":"analyzer","workdir":"relaycast","task":"List the top-level files in this directory and report what project this is.\nOutput: RELAYCAST_CHECKED\n","verification":{"type":"output_contains","value":"RELAYCAST_CHECKED"}},{"name":"check-dashboard","agent":"analyzer","workdir":"relay-dashboard","dependsOn":["check-relaycast"],"task":"List the top-level files and report what project this is.\nUse the output from the previous step for context: {{steps.check-relaycast.output}}\nOutput: DASHBOARD_CHECKED\n","verification":{"type":"output_contains","value":"DASHBOARD_CHECKED"}}]}]},"startedAt":"2026-03-10T06:13:44.390Z","createdAt":"2026-03-10T06:13:44.390Z","updatedAt":"2026-03-10T06:13:44.391Z"}} -{"kind":"step","row":{"id":"a58455d2a25e066529f34824","runId":"fc23c441426d5520b8dbfe65","stepName":"check-relaycast","agentName":"analyzer","stepType":"agent","status":"running","task":"List the top-level files in this directory and report what project this is.\nOutput: RELAYCAST_CHECKED\n","dependsOn":[],"retryCount":0,"createdAt":"2026-03-10T06:13:44.390Z","updatedAt":"2026-03-10T06:13:47.954Z","startedAt":"2026-03-10T06:13:47.954Z"}} -{"kind":"step","row":{"id":"a58455d2a25e066529f34824","runId":"fc23c441426d5520b8dbfe65","stepName":"check-relaycast","agentName":"analyzer","stepType":"agent","status":"failed","task":"List the top-level files in this directory and report what project this is.\nOutput: RELAYCAST_CHECKED\n","dependsOn":[],"retryCount":0,"createdAt":"2026-03-10T06:13:44.390Z","updatedAt":"2026-03-10T06:14:00.141Z","startedAt":"2026-03-10T06:13:47.954Z","error":"Verification failed for \"check-relaycast\": output does not contain \"RELAYCAST_CHECKED\" (token found only in task injection — agent must output it explicitly)","completedAt":"2026-03-10T06:14:00.141Z"}} -{"kind":"step","row":{"id":"4e0558fad3400f2c5f10657d","runId":"fc23c441426d5520b8dbfe65","stepName":"check-dashboard","agentName":"analyzer","stepType":"agent","status":"skipped","task":"List the top-level files and report what project this is.\nUse the output from the previous step for context: {{steps.check-relaycast.output}}\nOutput: DASHBOARD_CHECKED\n","dependsOn":["check-relaycast"],"retryCount":0,"createdAt":"2026-03-10T06:13:44.390Z","updatedAt":"2026-03-10T06:14:00.142Z"}} -{"kind":"run","row":{"id":"fc23c441426d5520b8dbfe65","workspaceId":"local","workflowName":"cross-repo-analysis","pattern":"dag","status":"failed","config":{"version":"1.0","name":"cross-repo-path-test","description":"E2E test for paths and workdir features (PR","paths":[{"name":"relaycast","path":"/Users/khaliqgant/Projects/relaycast","description":"Relaycast SDK repo"},{"name":"relay-dashboard","path":"/Users/khaliqgant/Projects/relay-dashboard","description":"Dashboard UI"}],"swarm":{"pattern":"dag","maxConcurrency":2,"channel":"path-test"},"agents":[{"name":"analyzer","cli":"claude","role":"Code analyzer","preset":"worker"}],"workflows":[{"name":"cross-repo-analysis","steps":[{"name":"check-relaycast","agent":"analyzer","workdir":"relaycast","task":"List the top-level files in this directory and report what project this is.\nOutput: RELAYCAST_CHECKED\n","verification":{"type":"output_contains","value":"RELAYCAST_CHECKED"}},{"name":"check-dashboard","agent":"analyzer","workdir":"relay-dashboard","dependsOn":["check-relaycast"],"task":"List the top-level files and report what project this is.\nUse the output from the previous step for context: {{steps.check-relaycast.output}}\nOutput: DASHBOARD_CHECKED\n","verification":{"type":"output_contains","value":"DASHBOARD_CHECKED"}}]}]},"startedAt":"2026-03-10T06:13:44.390Z","createdAt":"2026-03-10T06:13:44.390Z","updatedAt":"2026-03-10T06:14:00.143Z","completedAt":"2026-03-10T06:14:00.143Z","error":"Step \"check-relaycast\" failed: Step \"check-relaycast\" failed after 0 retries: Verification failed for \"check-relaycast\": output does not contain \"RELAYCAST_CHECKED\" (token found only in task injection — agent must output it explicitly)"}} -{"kind":"run","row":{"id":"ee39e6c435a567f189511078","workspaceId":"local","workflowName":"cross-repo-analysis","pattern":"dag","status":"pending","config":{"version":"1.0","name":"cross-repo-path-test","description":"E2E test for paths and workdir features (PR","paths":[{"name":"relaycast","path":"/Users/khaliqgant/Projects/relaycast","description":"Relaycast SDK repo"},{"name":"relay-dashboard","path":"/Users/khaliqgant/Projects/relay-dashboard","description":"Dashboard UI"}],"swarm":{"pattern":"dag","maxConcurrency":2,"channel":"path-test"},"agents":[{"name":"analyzer","cli":"claude","role":"Code analyzer","preset":"worker"}],"workflows":[{"name":"cross-repo-analysis","steps":[{"name":"check-relaycast","agent":"analyzer","workdir":"relaycast","task":"Run `pwd` and `ls` in the current directory. Report what project this is based on the files you see.\nAt the very end of your response, output the exact string: RELAY_SDK_VERIFIED\n","verification":{"type":"output_contains","value":"Relaycast"}},{"name":"check-dashboard","agent":"analyzer","workdir":"relay-dashboard","dependsOn":["check-relaycast"],"task":"Run `pwd` and `ls` in the current directory. Report what project this is.\nHere is context from a previous step: {{steps.check-relaycast.output}}\nAt the very end of your response, output the exact string: DASH_VERIFIED\n","verification":{"type":"output_contains","value":"dashboard"}}]}]},"startedAt":"2026-03-10T06:14:54.481Z","createdAt":"2026-03-10T06:14:54.481Z","updatedAt":"2026-03-10T06:14:54.481Z"}} -{"kind":"step","row":{"id":"69388e7f4987eb66fbb43914","runId":"ee39e6c435a567f189511078","stepName":"check-relaycast","agentName":"analyzer","stepType":"agent","status":"pending","task":"Run `pwd` and `ls` in the current directory. Report what project this is based on the files you see.\nAt the very end of your response, output the exact string: RELAY_SDK_VERIFIED\n","dependsOn":[],"retryCount":0,"createdAt":"2026-03-10T06:14:54.481Z","updatedAt":"2026-03-10T06:14:54.481Z"}} -{"kind":"step","row":{"id":"f301df3739ac624e5b250a5d","runId":"ee39e6c435a567f189511078","stepName":"check-dashboard","agentName":"analyzer","stepType":"agent","status":"pending","task":"Run `pwd` and `ls` in the current directory. Report what project this is.\nHere is context from a previous step: {{steps.check-relaycast.output}}\nAt the very end of your response, output the exact string: DASH_VERIFIED\n","dependsOn":["check-relaycast"],"retryCount":0,"createdAt":"2026-03-10T06:14:54.481Z","updatedAt":"2026-03-10T06:14:54.481Z"}} -{"kind":"run","row":{"id":"ee39e6c435a567f189511078","workspaceId":"local","workflowName":"cross-repo-analysis","pattern":"dag","status":"running","config":{"version":"1.0","name":"cross-repo-path-test","description":"E2E test for paths and workdir features (PR","paths":[{"name":"relaycast","path":"/Users/khaliqgant/Projects/relaycast","description":"Relaycast SDK repo"},{"name":"relay-dashboard","path":"/Users/khaliqgant/Projects/relay-dashboard","description":"Dashboard UI"}],"swarm":{"pattern":"dag","maxConcurrency":2,"channel":"path-test"},"agents":[{"name":"analyzer","cli":"claude","role":"Code analyzer","preset":"worker"}],"workflows":[{"name":"cross-repo-analysis","steps":[{"name":"check-relaycast","agent":"analyzer","workdir":"relaycast","task":"Run `pwd` and `ls` in the current directory. Report what project this is based on the files you see.\nAt the very end of your response, output the exact string: RELAY_SDK_VERIFIED\n","verification":{"type":"output_contains","value":"Relaycast"}},{"name":"check-dashboard","agent":"analyzer","workdir":"relay-dashboard","dependsOn":["check-relaycast"],"task":"Run `pwd` and `ls` in the current directory. Report what project this is.\nHere is context from a previous step: {{steps.check-relaycast.output}}\nAt the very end of your response, output the exact string: DASH_VERIFIED\n","verification":{"type":"output_contains","value":"dashboard"}}]}]},"startedAt":"2026-03-10T06:14:54.481Z","createdAt":"2026-03-10T06:14:54.481Z","updatedAt":"2026-03-10T06:14:54.482Z"}} -{"kind":"step","row":{"id":"69388e7f4987eb66fbb43914","runId":"ee39e6c435a567f189511078","stepName":"check-relaycast","agentName":"analyzer","stepType":"agent","status":"running","task":"Run `pwd` and `ls` in the current directory. Report what project this is based on the files you see.\nAt the very end of your response, output the exact string: RELAY_SDK_VERIFIED\n","dependsOn":[],"retryCount":0,"createdAt":"2026-03-10T06:14:54.481Z","updatedAt":"2026-03-10T06:14:58.034Z","startedAt":"2026-03-10T06:14:58.033Z"}} -{"kind":"step","row":{"id":"69388e7f4987eb66fbb43914","runId":"ee39e6c435a567f189511078","stepName":"check-relaycast","agentName":"analyzer","stepType":"agent","status":"completed","task":"Run `pwd` and `ls` in the current directory. Report what project this is based on the files you see.\nAt the very end of your response, output the exact string: RELAY_SDK_VERIFIED\n","dependsOn":[],"retryCount":0,"createdAt":"2026-03-10T06:14:54.481Z","updatedAt":"2026-03-10T06:15:12.833Z","startedAt":"2026-03-10T06:14:58.033Z","output":"**Project: Relaycast**\n\nThis is the **Relaycast** project, located at `/Users/khaliqgant/Projects/relaycast`. Based on the files present, it is a TypeScript monorepo (indicated by `turbo.json`, `tsconfig.base.json`, and `packages/` directory) that appears to be:\n\n- **A Cloudflare Workers-based service** — `wrangler.toml` and `wrangler.observer-router.toml` indicate Cloudflare Workers deployment\n- **A multi-package monorepo** — managed with Turborepo (`turbo.json`) and npm (`package-lock.json`)\n- **An API service** — has an `openapi.yaml` spec\n- **Uses Drizzle ORM** — `drizzle.config.ts` for database management\n- **Has a website/docs** — `site/` directory\n- **Publishable as an MCP server** — `smithery.yaml` and `prpm.json` suggest it's a Model Context Protocol (MCP) server package, likely for inter-agent communication/relay\n\nRELAY_SDK_VERIFIED\n","completedAt":"2026-03-10T06:15:12.833Z"}} -{"kind":"step","row":{"id":"f301df3739ac624e5b250a5d","runId":"ee39e6c435a567f189511078","stepName":"check-dashboard","agentName":"analyzer","stepType":"agent","status":"running","task":"Run `pwd` and `ls` in the current directory. Report what project this is.\nHere is context from a previous step: {{steps.check-relaycast.output}}\nAt the very end of your response, output the exact string: DASH_VERIFIED\n","dependsOn":["check-relaycast"],"retryCount":0,"createdAt":"2026-03-10T06:14:54.481Z","updatedAt":"2026-03-10T06:15:12.835Z","startedAt":"2026-03-10T06:15:12.835Z"}} -{"kind":"step","row":{"id":"f301df3739ac624e5b250a5d","runId":"ee39e6c435a567f189511078","stepName":"check-dashboard","agentName":"analyzer","stepType":"agent","status":"completed","task":"Run `pwd` and `ls` in the current directory. Report what project this is.\nHere is context from a previous step: {{steps.check-relaycast.output}}\nAt the very end of your response, output the exact string: DASH_VERIFIED\n","dependsOn":["check-relaycast"],"retryCount":0,"createdAt":"2026-03-10T06:14:54.481Z","updatedAt":"2026-03-10T06:15:17.521Z","startedAt":"2026-03-10T06:15:12.835Z","output":"\n\n**Project Report**\n\nThis is the **relay-dashboard** project, located at `/Users/khaliqgant/Projects/relay-dashboard`.\n\nDASH_VERIFIED\n","completedAt":"2026-03-10T06:15:17.521Z"}} -{"kind":"run","row":{"id":"ee39e6c435a567f189511078","workspaceId":"local","workflowName":"cross-repo-analysis","pattern":"dag","status":"completed","config":{"version":"1.0","name":"cross-repo-path-test","description":"E2E test for paths and workdir features (PR","paths":[{"name":"relaycast","path":"/Users/khaliqgant/Projects/relaycast","description":"Relaycast SDK repo"},{"name":"relay-dashboard","path":"/Users/khaliqgant/Projects/relay-dashboard","description":"Dashboard UI"}],"swarm":{"pattern":"dag","maxConcurrency":2,"channel":"path-test"},"agents":[{"name":"analyzer","cli":"claude","role":"Code analyzer","preset":"worker"}],"workflows":[{"name":"cross-repo-analysis","steps":[{"name":"check-relaycast","agent":"analyzer","workdir":"relaycast","task":"Run `pwd` and `ls` in the current directory. Report what project this is based on the files you see.\nAt the very end of your response, output the exact string: RELAY_SDK_VERIFIED\n","verification":{"type":"output_contains","value":"Relaycast"}},{"name":"check-dashboard","agent":"analyzer","workdir":"relay-dashboard","dependsOn":["check-relaycast"],"task":"Run `pwd` and `ls` in the current directory. Report what project this is.\nHere is context from a previous step: {{steps.check-relaycast.output}}\nAt the very end of your response, output the exact string: DASH_VERIFIED\n","verification":{"type":"output_contains","value":"dashboard"}}]}]},"startedAt":"2026-03-10T06:14:54.481Z","createdAt":"2026-03-10T06:14:54.481Z","updatedAt":"2026-03-10T06:15:17.523Z","completedAt":"2026-03-10T06:15:17.523Z"}} diff --git a/.gitignore b/.gitignore index ef4c5a4e1..156d4a24d 100644 --- a/.gitignore +++ b/.gitignore @@ -76,3 +76,4 @@ __pycache__/ *.pyo *.egg-info/ .pytest_cache/ +.agent-relay/ From 16a3a6301f3b6aea50b95d1321e1d30ea9282e63 Mon Sep 17 00:00:00 2001 From: Khaliq Date: Tue, 10 Mar 2026 10:33:31 +0100 Subject: [PATCH 5/6] fix(workflows): worktree steps respect workdir field executeWorktreeStep now uses resolveStepWorkdir() like deterministic and agent steps, so worktree operations happen in the correct repo when workdir references a named path. --- packages/sdk/src/workflows/runner.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/sdk/src/workflows/runner.ts b/packages/sdk/src/workflows/runner.ts index 380aebdff..99cfadb7b 100644 --- a/packages/sdk/src/workflows/runner.ts +++ b/packages/sdk/src/workflows/runner.ts @@ -2034,6 +2034,9 @@ export class WorkflowRunner { : path.join('.worktrees', step.name); const createBranch = step.createBranch !== false; + // Resolve workdir for worktree steps (same as deterministic/agent steps) + const stepCwd = this.resolveStepWorkdir(step) ?? this.cwd; + if (!branch) { const errorMsg = 'Worktree step missing required "branch" field'; await this.markStepFailed(state, errorMsg, runId); @@ -2043,7 +2046,7 @@ export class WorkflowRunner { try { // Build the git worktree command // If createBranch is true and branch doesn't exist, use -b flag - const absoluteWorktreePath = path.resolve(this.cwd, worktreePath); + const absoluteWorktreePath = path.resolve(stepCwd, worktreePath); // First, check if the branch already exists const checkBranchCmd = `git rev-parse --verify --quiet ${branch} 2>/dev/null`; @@ -2052,7 +2055,7 @@ export class WorkflowRunner { await new Promise((resolve) => { const checkChild = cpSpawn('sh', ['-c', checkBranchCmd], { stdio: 'pipe', - cwd: this.cwd, + cwd: stepCwd, env: { ...process.env }, }); checkChild.on('close', (code) => { @@ -2080,7 +2083,7 @@ export class WorkflowRunner { const output = await new Promise((resolve, reject) => { const child = cpSpawn('sh', ['-c', worktreeCmd], { stdio: 'pipe', - cwd: this.cwd, + cwd: stepCwd, env: { ...process.env }, }); From eed02a1e1c9339459ad3240b1b7f25c8ff673e97 Mon Sep 17 00:00:00 2001 From: Khaliq Date: Tue, 10 Mar 2026 10:36:17 +0100 Subject: [PATCH 6/6] docs: clarify workdir defaults to runner cwd when omitted --- packages/sdk/src/workflows/types.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/sdk/src/workflows/types.ts b/packages/sdk/src/workflows/types.ts index 1e6375b61..93e86f111 100644 --- a/packages/sdk/src/workflows/types.ts +++ b/packages/sdk/src/workflows/types.ts @@ -124,7 +124,8 @@ export interface AgentDefinition { /** Working directory for this agent, resolved relative to the YAML file. */ cwd?: string; /** Sets this agent's working directory to a named entry from the top-level `paths` array. - * Mutually exclusive with `cwd`. */ + * Mutually exclusive with `cwd`. If omitted, the agent runs in the runner's + * working directory (the directory containing the workflow YAML file). */ workdir?: string; /** Additional paths the agent needs read/write access to. */ additionalPaths?: string[]; @@ -258,7 +259,9 @@ export interface WorkflowStep { // ── Deterministic step fields ────────────────────────────────────────────── /** Shell command to execute (required for deterministic steps). */ command?: string; - /** Sets this step's working directory to a named entry from the top-level `paths` array. */ + /** Sets this step's working directory to a named entry from the top-level `paths` array. + * If omitted, the step inherits the agent's workdir, or falls back to the runner's + * working directory. */ workdir?: string; /** Fail if command exit code is non-zero. Default: true. */ failOnError?: boolean;