Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/conductor/engine/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -1448,6 +1448,23 @@ async def _execute_loop(self, current_agent_name: str) -> dict[str, Any]:
"stderr": script_output.stderr,
"exit_code": script_output.exit_code,
}
# Auto-parse JSON stdout: if stdout is valid JSON
# object, merge its fields into output so they're
# accessible as output.field_name in templates and
# route conditions (like LLM structured outputs).
try:
parsed = json.loads(script_output.stdout)
if isinstance(parsed, dict):
shadowed = set(parsed.keys()) & set(output_content.keys())
if shadowed:
logger.debug(
"Script '%s' JSON output shadows built-in fields: %s",
agent.name,
", ".join(sorted(shadowed)),
)
output_content.update(parsed)
except (json.JSONDecodeError, ValueError):
pass
self.context.store(agent.name, output_content)
self.limits.record_execution(agent.name)
self.limits.check_timeout()
Expand Down
40 changes: 40 additions & 0 deletions tests/test_engine/test_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,46 @@ def mock_handler(agent, prompt, context):
# Workflow.input.goal should not be in agent2's context since it's not in input list
assert "other" not in agent2_context.get("workflow", {}).get("input", {})

@pytest.mark.asyncio
async def test_script_json_stdout_parsed_into_output(self) -> None:
"""Test that script stdout containing JSON is auto-parsed into output fields."""
config = WorkflowConfig(
workflow=WorkflowDef(name="json-script", entry_point="detector"),
agents=[
AgentDef(
name="detector",
type="script",
command="pwsh",
args=[
"-Command",
'Write-Output \'{"plan_exists": true, "route": "planning"}\'; exit 0',
],
routes=[
RouteDef(to="planner", when="route == 'planning'"),
RouteDef(to="$end"),
],
),
AgentDef(
name="planner",
type="script",
command="pwsh",
args=["-Command", "Write-Output 'done'; exit 0"],
routes=[RouteDef(to="$end")],
),
],
)

provider = CopilotProvider(mock_handler=lambda a, p, c: {})
engine = WorkflowEngine(config, provider)
await engine.run({})

det = engine.context.agent_outputs["detector"]
assert det["plan_exists"] is True
assert det["route"] == "planning"
assert "stdout" in det
assert det["exit_code"] == 0
assert "planner" in engine.context.agent_outputs


class TestWorkflowEngineRouting:
"""Tests for workflow routing."""
Expand Down
Loading