Agent, agent(), agent_async(), and Agent.arun share the same execution core. Ordinary Agent methods and managed Program functions require no per-function decorator. The unified Agent and Context contract owns configuration, context, and scope lifetime.

Refactor Plan · Design Only

Unify Agent execution semantics and refactor Self Programming Workflow

Programs use agent() or configured Agent calls. Independent sessions, background Jobs and sub-agent branches remain owned by internal adapters, but both paths must enter the same Agent loop instead of maintaining separate planning, tool-call, model-override and error semantics.

Goal: one public Agent APIScope: Runtime / Workflow / DAG / Chat UIMigration: staged and reversibleStatus: Workflow orchestration migrated · App/DAG acceptance pending

1. Conclusions and boundaries

This refactoring adds no third Agent abstraction and does not wrap the session Job API in another public function. Public semantics remain in agent(); persistence and branching belong to the execution environment, not the Workflow authoring API.

Shared execution entry

agent() and configured Agent calls enter the same autonomous tool loop.

Ordinary Agentic Functions do not call it and permit only one llm(); the full classification contract is in Programming model and categories. Within a Workflow, agent() runs in the current Runtime and forms parent-child Call relationships.

Internal session adapter

run_agent_turn() serves only Chat, CLI, background Jobs and genuinely independent sub-agents. It creates session branches, claims workers, restores Jobs and moves session heads; Programs must not import it directly.

Included in this refactoringUnified Agent loops, Workflow selection/creation/repair, DAG ownership, structured output, error propagation, migration tests and UI display contracts.
Not introduced by this refactoringA new Workflow DSL, graph execution engine, second Agent profile system, extra persistence database or display of hidden chain-of-thought.

2. Current implementation: verified facts

Currentagent() Already uses the current Runtime

openprogram/agentic_programming/agent.py calls runtime.exec(..., execution_kind="agent"), making it naturally suitable for a Workflow DAG.

Currentrun_agent_turn() is a persistent Job entry

It creates/resumes session Jobs and manages worker claims, branches, heads, archives and chain depth.

Resolved in workflowWorkflow creation previously used the wrong layer

create_workflow authoring runs through _run_planner_turn() → agent() and belongs to the current Workflow’s public Agent Call; run_agent_turn() remains exclusive to Chat/CLI/Jobs.

CurrentPublic agent() already supports structured results

Public agent() Already passes response_format through to the current Runtime and returns validated JSON values unchanged; the Workflow planner still parses JSON text.

LocationCurrent responsibilityProblemTarget
agentic_programming.agentTool loop inside the current RuntimeIncomplete parameters and unclear DAG labelsExtend into the sole public Agent contract
agent.sub_agent_runSession branches and durable JobsCalled directly by internal Workflow planningRetain only as an internal lifecycle adapter
Workflow management and entriessearch_workflows, create_workflow, revise_workflow, and workflows/auto_workflow.pyThe old composite entry mixed distinct side effectsThe first three remain reusable management functions; the full automatic process resides in workflows/ and stays independently registered
Runtime.execShared lower-level LLM/tool loopPublic parameters are not fully exposedRemain the sole Agent-loop core without public session semantics

3. Target architecture: two adapter entries, one execution core

PROGRAM EXECUTIONWorkflow / ApplicationCall public agent(); ordinary Agentic Functions call only llm() SESSION EXECUTIONChat / CLI / sub-agent / background JobCall internal run_agent_turn(); manage sessions and durable Jobs public agent()prompt · tools · profile · response format · limits · label internal run_agent_turn()session · branch · Job · recovery · head · archive ONE AGENT LOOP KERNELRuntime.exec → AgentSession → agent_loop → provider streamSame profile resolver · tool resolver · retry/cancel/timeout · usage · error normalization · Call eventsNo second executor class; reuse existing Runtime and dispatcher execution inputs

Differences exist only in lifecycle before/after execution: Programs reuse the current Runtime; sessions create durable Jobs first. Model loops, tool selection and error semantics remain identical.

4. Fixed hierarchy of four execution primitives

Workflow is the highest layer and may directly choose llm(), agent(), goal() or ordinary Python Functions.goal() builds on agent(); agent() builds on llm(). These are dependencies; a Workflow need not traverse every layer.

Workflow / ApplicationSelect any capability below as required by the task Python FunctionsDeterministic execution llm()One model call agent()Autonomous tool loop goal()Agent execution + LLM judgment Goal internally calls agentEach agent iteration calls llm/provider
PrimitivePublic semanticsPersistent session?DAG record
llm()Exactly one logical model request; structured output supportedNo; inherits current RuntimeOne LLM Call
agent()Model/tool loop within one task; returns final text or structured resultsNo; inherits current RuntimeOne Agent Call containing LLM and Function Calls
goal()Attempt with an Agent, then independently judge goal satisfaction. Programs isolate pre-call history by default; /goal explicitly supplies the current session viewYes; writes the same GoalState in its owning sessionGoal → Agent → Goal judge
run_agent_turn()Internal session lifecycle adapter, not a Program primitiveYesIndependent Agent Call associated with session/branch

Target public signature

def agent(
    prompt: str | list[dict],
    *,
    description: str = "",
    agent_id: str = "",
    model: str = "",
    effort: str = "",
    tools: Sequence[str] | None = None,
    response_format: dict | JsonSchemaOutput | None = None,
    max_iterations: int = 20,
    timeout_s: float | None = None,
) -> str | dict | list | int | float | bool | None:
    ...

description affects only visible labels and Call metadata; agent_id reuses the existing Agents-page profile resolver; explicit model/tools overrides corresponding profile fields. The public signature adds no session_id, branch_from, advance_head, archive_when_done or run_in_background.

5. Target Self Programming Workflow process

Public behavior is split into search_workflows, create_workflow, revise_workflow and the user-triggered entry auto_workflow(task). Chat Agents directly call the first three capabilities or a specific Workflow as needed; only a manually requested complete flow enters auto_workflow. This section describes only their use of public agent(); the complete contract is defined by Discovery, creation and automatic execution.

auto_workflow(task) AGENT · SELECT REUSE OR CREATEsearch_workflows → read candidates → choose reuse / createNever automatically revise published Workflows; all creation enters create_workflow Deterministic validator + publisherpackage · Agent method export · imports · policy · source revision create validation failed AGENT · BOUNDED AUTHOR RETRYUse explicit diagnostics to rewrite the unpublished candidatePart of create pre-publication validation only; does not modify published Workflows validated EXECUTE SELECTED WORKFLOWDirectly call the published Agent methodAll nested llm / agent / goal / Functions enter the same DAG summary() · Workflow handoff2–3 sentence overview + key steps + completion status; result files remain in the project directory
The Selection Agent emits only reuse/create decisions and writes no files. After create is chosen, create_workflow author and deterministic validator generate/publish the multi-file Python package; explicit edits are independently owned by revise_workflow .
StageAllowed capabilitiesExplicit outputObservability
Search and selectionRead-only catalog and candidate metadataPinned revision or create decisionSearch function and one selection-Agent node
Validation and publicationPure Python validation and file operationsRevision, dependency inventory and validation diagnosticsFunction Call node
Creation retrySame unpublished candidate, explicit diagnostics and bounded roundsNew candidate passing gatescreate_workflow Agent nodes under
ExecutionPrograms declared by the WorkflowFile deliverables or explicit return_resultComplete nested DAG
SummaryRun events and deliverable metadataProcess summary without copying the complete bodyConclusion area

6. DAG and chat display contract

Primary acceptance is not merely a function returning JSON: users must see how a Workflow selected and created work, which Programs actually ran and where an error occurred.

auto_workflowuser-only root Agentreuse / create only Validatorpublish llm_knowledge_researchselected Workflow LLMreasoning summary search Programscatalog / graph decision outputreuse or create goaltask objective agentresearch loop llmjudge web_searchProgram call read / extractProgram calls LLMsynthesis summary + delivery metadata
UI levelDefault displayExpanded display
Top-level WorkflowName, state, duration, step count and complete ConclusionSelection/authoring Agents, validation, actual Workflow and summary
AgentDescription label, state and iteration countModel-output summaries and actual Program calls; no hidden chain-of-thought
Program CallName, argument summary and stateInput, output, errors and side-panel details
sub-agent sessionSeparate navigable rowSwitch to the child session; returning preserves the same row rather than another card system

7. Failures, recovery and state consistency

Failure pointHandling ruleUser-visible state
Provider/model lacks structured outputAfter capability detection, use text JSON + local schema validation; never overwrite an internal LLM error with Workflow completedCorresponding Agent/LLM Call error; top-level Workflow failed
Candidate Workflow validation failsPass deterministic diagnostics to bounded public agent() repair; fail at the limitEach repair is an expandable Agent node
Worker restartOnly session/Job paths use durable recovery; in-Program Agents terminate with the parent Call and follow the parent Workflow retry policyDo not retain old running states indefinitely
User cancellationPropagate one cancellation token to Runtime, provider and tools; terminate child calls before closing the parent WorkflowConsistent cancelled/interrupted states, never completed
Tool or child Workflow failurePreserve original error payload, caller and partial artifacts; summary must not claim completionFailed nodes and Conclusion agree
Historical run recoveryOld SINGLE/code.py state serves only historical recovery; every new task must use project selection/creationOld records remain readable; new runs create no legacy project

8. Staged migration plan

Each stage first adds tests reproducing the current gap, then modifies the corresponding layer. Never change public API, session lifecycle, Workflow layout and chat visual styles in one stage.

01

Freeze execution contracts

  • Record current agent(), run_agent_turn() and Runtime call graphs.
  • Add architecture tests: Programs must not import agent.sub_agent_run.
  • Add real registered-schema tests fixing public parameters of the four Workflow management functions.
Gate: add only failing tests and documentation; no behavior change.
02

Complete public agent()

  • Pass through response_format, content blocks, description and Agent profile.
  • Reuse profile/tool resolvers without a new configuration format.
  • Add an Agent wrapper Call for stable ownership of LLM/tool nodes.
Gate: public-agent unit tests, DAG-caller tests and provider-fallback tests.
03

Separate Workflow public behaviors

  • search_workflows remains deterministic and read-only.
  • auto_workflow selection Agent chooses reuse/create only; no file writes or automatic revision.
  • Authoring, writing and pre-publication validation belong to create_workflow and revise_workflow.
Gate: integration tests for four independent search/create/revise/auto public entries.
04

Restrict the session adapter

  • run_agent_turn() remains internal, supporting branches, Jobs, recovery and archive.
  • Confirm dispatcher ultimately uses the same Agent-loop core as public agent.
  • Remove duplicate model defaults, tool resolution and error normalization.
Gate: background recovery, cancellation, branch/head and spawn-caller tests.
05

Migrate all Programs

  • Scan openprogram/programs/ for internal Agent API and direct-loop calls.
  • Workflows and Applications consistently use public agent(); ordinary Agentic Functions permit only one llm().
  • Retain the vanilla sub-agent tool as the product entry for the session adapter.
Gate: static dependency checks + Programs catalog/graph regressions.
06

DAG and chat display

  • Backend events add execution_kind, label, caller, iteration and artifact metadata.
  • Frontend renders the same Runtime hierarchy without inferring a separate structure.
  • Conclusion, footer and collapse behavior reuse existing shared components.
Gate: mapper, RuntimeBlock, reload and actual-App DOM acceptance.
07

Compatibility cleanup and release

  • Read-only recovery for old runs; prohibit legacy SINGLE for new runs.
  • Delete unused planner helpers and duplicate test fixtures.
  • Update design navigation, API documentation and Programs dependency graphs.
Gate: wheel packages current web/out; default 18100 App/Web/TUI share the same source.
08

Controlled rollout

  • Run focused component tests first, then required complete suites.
  • Accept one real reuse, create, repair and cancel session each.
  • Retain the old recovery reader for one compatibility period; never write the old format again.
Gate: record all acceptance evidence in implementation status, not as retroactive design assumptions.

9. Compatibility, retention and removals

ObjectTreatmentReason
agent()Retain and extend; old arguments continue workingAlready the correct public primitive within ambient Runtime
run_agent_turn()Retain as internal; prohibit Programs importsChat/CLI/background genuinely need durable sessions
_run_planner_turn()Workflow selection/authoring calls public agent()Planning inside Programs no longer imports run_agent_turnRetain as a agent(tools=PLANNER_TOOLS) thin wrapper; removable later
_request_project_decision()Replace with actual tool behavior of a single orchestration AgentSelection JSON followed by authoring creates two Agent semantics and a disconnected DAG
legacy SINGLE / code.pyRead-only recovery; prohibit creationPreserve historical records without creating unmaintainable single-file Workflows
Workflow project packageContinue standard Python packages and explicit Agent method or function exportsSupport multi-file maintenance, Git revisions, static dependency graphs and direct Python calls
Agents profileReuse existing Agents configuration/resolverAvoid a second model/tool configuration inside Programs

10. Acceptance matrix

API and registration

  • Registry contains only search_workflows, create_workflow, revise_workflow and auto_workflow; category directories are not registered.
  • agent() has stable model/tools/profile override order.
  • Structured output validates with providers supporting and lacking native schemas.

Call structure

  • Selection belongs to auto_workflow; authoring and pre-publication retries belong to create_workflow or revise_workflow.
  • All Programs called by a Workflow appear in the DAG.
  • Direct/manual Workflows are not incorrectly attached to the prior assistant through predecessor.

Lifecycle

  • Parent cancellation terminates Agent, LLM and tools.
  • Session Job restart recovers only the session path.
  • New runs never generate SINGLE; old runs resume or return an explicit incompatibility error.

UI

  • Collapsed state shows Workflow title, Conclusion and footer.
  • Expand progressively, not all children at once.
  • App/Web/TUI read the same default profile and worker at 18100.

Suggested focused checks

pytest -q tests/component/agent/test_agentic_workflow.py
pytest -q tests/unit/programs/dag/test_runtime_exec_dag.py
pytest -q tests/unit/agent/workflows/test_agentic_programming_goal.py
pytest -q tests/component/agent/jobs/test_async_job_recovery.py
pytest -q tests/unit/agent/jobs/test_spawn_caller_invariant.py
pytest -q tests/component/webui/abilities/test_programs_explorer.py
npm --prefix web run check:chat-ui
npx --prefix web tsc --noEmit
python -m scripts.docs_site.checklinks
git diff --check
Unit-test success is insufficient for completion. Run reuse, create, repair and cancel sessions in the packaged App and inspect reloaded DOM, DAG caller, state and Conclusion.

11. Framework comparison and decisions

Compare only capabilities directly relevant to this architectural decision. OpenProgram introduces no new third-party orchestration runtime.

ReferenceAdoptedNot adopted
OpenAI Agents SDK · Running agentsRunner owns the model/tool loop of one logical turn; max turns, sessions and hooks are separate.Do not use Runner as a second OpenProgram runtime; retain current Runtime/dispatcher.
OpenAI Agents SDK · ToolsPython function tools and agent-as-tool are explicit calls; parents can observe child execution.Do not implicitly convert Workflows into handoffs; they remain Python packages.
OpenAI Agents SDK · OrchestrationDistinguish a manager calling a child Agent from handing control to another Agent.Inside a Program, agent() does not automatically create session branches; only product-level sub-agent actions switch sessions.
LangGraph · Workflows and agentsWorkflows are predefined code paths; Agents dynamically choose tools locally.No new graph DSL; static call graphs derive from Python imports and explicit entry identities .
LangGraph · PersistencePersistence, threads, checkpoints and interruption recovery should be explicit lifecycle capabilities.Do not force every in-Program Agent into a persistent thread; only the session adapter uses durable Jobs.

12. Implementation status

Verified currentExisting public llm(), agent(), goal()

All three already use ambient Runtime; retain the hierarchy direction.

Verified currentRuntime already supports most target parameters

response_format, tools, model, effort, timeout and execution_kind capabilities already exist.

Verified currentWorkflow selection/authoring already uses public agent()

workflow._run_planner_turn() calls openprogram.agentic_programming.agent()(restricted PLANNER_TOOLS); auto selection has a AUTO_DECISION_ATTEMPTS limit. The authoring design owns the four registered entries and source-classification contract.

PartialWorkflow-embedded agent() still uses the session adapter

legacy code.py In the execution path, _agent_function() remains bound to the session turn; project-snapshot execution already uses public agent().

PendingUnified DAG and App visual acceptance remain incomplete

Component tests cover reuse/create/revise/pin/resume; selection-Agent nodes, Conclusion and reload behavior in the default App remain unverified.

Verified currentpublic agent() Structured-output passthrough

Public agent() Accepts a bare JSON Schema or JsonSchemaOutput, returning Runtime-validated objects, arrays, scalars or null unchanged. Without structured-output requests, retain text and legacy {"text": ...} compatibility; Workflow-planner migration is outside this implementation.