Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
f67217a
feat(flow): merge cuga-flo BPMN-driven flow engine into cuga-agent
liorlimonad May 25, 2026
bd23f16
fix(merge): correct missed changes from cuga-flo inspection
liorlimonad May 25, 2026
122c2e5
docs(flow): update CUGA FLO README with MCP bridge, WorkflowEngine, a…
liorlimonad May 26, 2026
bf134ba
docs(flow): add cugaflo logo image
liorlimonad May 26, 2026
84a9489
docs(flow): clarify CUGA FLO architecture separation and MCP engine i…
liorlimonad May 26, 2026
4629c85
docs(flow): reframe overview to articulate the BPM vs LLM-planner tra…
liorlimonad May 26, 2026
856bbce
docs(flow): revise overview to articulate BPM/LLM tradeoff and CUGA F…
liorlimonad May 26, 2026
3c35b1c
feat(flow): add REMOVE_NODE and ADD_NODE hook actions
liorlimonad May 26, 2026
6035a47
feat(flow): implement runtime graph rebuild for REMOVE_NODE and ADD_N…
liorlimonad May 26, 2026
67ba28e
feat(flow): replace replay with direct resume for REMOVE_NODE/ADD_NOD…
liorlimonad May 26, 2026
71d2e80
docs(flow): clarify hook action separation — CUGA FLO instructs, engi…
liorlimonad May 26, 2026
234dd79
docs(flow): reframe hooks as edge annotations, not graph nodes
liorlimonad May 26, 2026
5d234cc
feat(flow): enforce one hook per edge, remove priority [skip ci]
liorlimonad May 26, 2026
a194e92
docs(flow): remove stale BPMN diagram note from hooks description [sk…
liorlimonad May 26, 2026
35658a1
docs(flow): move all LangGraph-specific text into LangGraph notes [sk…
liorlimonad May 26, 2026
56ee925
docs(flow): restore two-step DecisionAgent description as general con…
liorlimonad May 26, 2026
87fcd44
docs(flow): elaborate demo apps with their specific CUGA FLO capabili…
liorlimonad May 26, 2026
029ad38
docs(flow): restore demo app descriptions alongside highlights [skip ci]
liorlimonad May 26, 2026
fbcad04
feat(trip-planner): make Choose type of planner a task agent for NL p…
liorlimonad May 26, 2026
4f293a6
feat(trip-planner): update BPMN for choose type of planner task agent…
liorlimonad May 26, 2026
ca663a7
fix(trip-planner): add output_mapping to write planning_preference ba…
liorlimonad May 26, 2026
35d7006
fix(flow): parse JSON output before applying output_mapping to proces…
liorlimonad May 26, 2026
1224c67
fix(flow): wire output_mapping and input_mapping from YAML into TaskA…
liorlimonad May 26, 2026
9ed9e6b
fix(flow): robust JSON extraction in output_mapping; add gateway rout…
liorlimonad May 26, 2026
874dbb5
docs(paper): fix Figure 2 TikZ layout overlaps [skip ci]
liorlimonad May 26, 2026
0f9edd9
docs(paper): fix remaining Figure 2 overlaps [skip ci]
liorlimonad May 26, 2026
1a44fed
docs: update lifecycle diagrams and add sequence diagram to paper [sk…
liorlimonad May 26, 2026
63e79fe
docs(paper): fix Figure 2 node overlaps; merge hook section into Flow…
liorlimonad May 26, 2026
0ddf97f
fig2: nested CugaAgent in agents, hook as label, remove BPMN parser b…
liorlimonad May 26, 2026
83b82bc
fix fig2: use align=center on floating text nodes to allow \\ in para…
liorlimonad May 26, 2026
478d007
fig2: move FlowAgent label inside box, shorten to 'FlowAgent' [skip ci]
liorlimonad May 26, 2026
f66a819
fig2: add condition-eval circle with arrow inside DecisionAgent box […
liorlimonad May 26, 2026
94ea93b
remove docs/tex directory and paper file [skip ci]
liorlimonad May 26, 2026
45ed976
Remove priority from hooks — one hook per edge, priority has no meaning
liorlimonad May 27, 2026
2075cd2
fix(flow_agent_inline): start web UI with published config and correc…
liorlimonad May 27, 2026
a873dba
fix(hook-policy): tighten audit hook to exact string match on applica…
liorlimonad May 27, 2026
ed19cad
fix(agent_loop): restore FlowAgent step draining in run_stream
liorlimonad May 27, 2026
a04dfe2
fix(config): add missing code_executor_timeout validator with default…
liorlimonad May 27, 2026
8e02e06
fix(supervisor): handle FlowAgent type in delegation so loan_flow_age…
liorlimonad May 27, 2026
64ca750
fix: hook evaluator never called due to dead code indentation bug [sk…
liorlimonad May 27, 2026
7ab591f
docs(loan-processor): remove pre-conditions block from task policy [s…
liorlimonad May 27, 2026
8e6a71c
docs(rejection-handler): remove pre-conditions block from task policy…
liorlimonad May 27, 2026
fe401fd
fix(loan-approval): map credit_score from task output to process vari…
liorlimonad May 28, 2026
9fae39d
chore: merge main into cugaflo — resolve index.html bundle conflict […
liorlimonad May 28, 2026
4f7e14b
fix(flow_agent_inline): add default starter so homescreen greeting is…
liorlimonad May 28, 2026
7b7ffd2
docs(diagrams): update lifecycle diagram to reflect current project s…
liorlimonad May 28, 2026
c431f19
docs(diagrams): show FlowAgent instantiating LangGraphWorkflowEngine …
liorlimonad May 28, 2026
57296c3
docs(diagrams): show full FlowAgent creation chain in STARTUP section…
liorlimonad May 28, 2026
ddda516
docs(diagrams): show explicit creation of ProcessRegistry and MCPFlow…
liorlimonad May 28, 2026
404e8c0
refactor(flow-config): move MCPFlowBridge and LangGraphWorkflowEngine…
liorlimonad May 28, 2026
d207f72
refactor(engine): engine registers itself with bridge via register_wi…
liorlimonad May 28, 2026
feed151
refactor(engine): LangGraphWorkflowEngine self-registers with bridge …
liorlimonad May 28, 2026
381e9bf
docs(diagrams): show all ProcessRegistry service calls in lifecycle d…
liorlimonad May 28, 2026
1b9eca5
Move ProcessRegistry behind MCPFlowBridge [skip ci]
liorlimonad May 28, 2026
4788821
Move ProcessRegistry to the right of MCPFlowBridge in sequence diagra…
liorlimonad May 28, 2026
ed547dd
Add ProcessRegistry.register_flow() to encapsulate pre-loaded registr…
liorlimonad May 28, 2026
67f4d61
Update sequence diagrams: FC->>Reg now shows register_flow() [skip ci]
liorlimonad May 28, 2026
21a7724
Delegate YAML+BPMN parsing from FlowConfig to ProcessRegistry.registe…
liorlimonad May 28, 2026
3d05385
Mediate register_flow through MCPFlowBridge.load_flow() [skip ci]
liorlimonad May 28, 2026
5f18232
ProcessRegistry self-registers with MCPFlowBridge on construction [sk…
liorlimonad May 28, 2026
7e184cf
Show ProcessRegistry services in sequence diagram [skip ci]
liorlimonad May 28, 2026
b4dc080
Remove unused ProcessRegistry.register() and register_from_directory(…
liorlimonad May 28, 2026
7220b50
Make get_definition private; remove FlowAgent.get_process_info() [ski…
liorlimonad May 28, 2026
f4ce011
Rename get_flow_config to get_flow_annotations [skip ci]
liorlimonad May 28, 2026
42cb306
Show FA->>Reg: get_flow_annotations in sequence diagram [skip ci]
liorlimonad May 28, 2026
3603a08
Mediate get_flow_annotations from FlowAgent through MCPFlowBridge [sk…
liorlimonad May 28, 2026
780cbf5
Drop redundant get_bpmn_process call in invoke(); run_process returns…
liorlimonad May 28, 2026
8cbe718
Show TaskAgent and DecisionAgent creation in startup sequence diagram…
liorlimonad May 28, 2026
02a9754
Engine fetches BPMNProcess via MCP instead of receiving it from bridg…
liorlimonad May 28, 2026
4c53e39
Engine fetches flow annotations from registry via bridge instead of F…
liorlimonad May 28, 2026
7dabed1
FlowAgent calls bridge.run_process() directly instead of get_client()…
liorlimonad May 28, 2026
8880a09
Drop registry arg from FlowAgent — bridge already holds it [skip ci]
liorlimonad May 28, 2026
10b2fb5
Name MCPFlowBridge instance 'cuga-flo-bridge' at construction [skip ci]
liorlimonad May 28, 2026
5dfd751
fix: restore to_flow_agent() to not pass registry to FlowAgent [skip ci]
liorlimonad May 28, 2026
2328b1d
add ordo MCP-2-MCP mediator, YAML schemas, and high-level sequence di…
liorlimonad May 31, 2026
75f1f90
add CLAUDE.md with git and environment conventions [skip ci]
liorlimonad May 31, 2026
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
7 changes: 7 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Git

Always include `[skip ci]` in every git commit message.

# Environment

If `cuga start` is not recognized, remind the user to run `source .venv/bin/activate` first.
113 changes: 113 additions & 0 deletions docs/diagrams/cuga-flo-high-level-sequence.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# CUGA FLO — High-Level Sequence Diagram

```mermaid
sequenceDiagram
participant App as Application
participant FA as FlowAgent
participant TA as TaskAgent
participant DA as DecisionAgent
participant Bridge as MCPFlowBridge
participant WE as WorkflowEngine

rect rgb(230, 245, 255)
Note over App,WE: INVOCATION — FlowAgent triggers WorkflowEngine via MCPFlowBridge
App->>FA: invoke(input_data, process_variables)
FA->>Bridge: run_process(process_key, initial_inputs)
Note over Bridge,WE: WorkflowEngine exposes run_process to CUGA FLO
Bridge->>WE: _run_via_mcp(process_key, initial_inputs)
end

rect rgb(230, 255, 235)
Note over App,WE: EXECUTION — WorkflowEngine calls back into MCPFlowBridge

alt Task Node
Note over Bridge,WE: MCPFlowBridge exposes execute_task to WorkflowEngine
WE->>Bridge: execute_task(task_id, process state)
Bridge->>FA: _handle_task(task_id, process state)
FA->>TA: execute(state, task_input)
TA-->>FA: {status, output}
FA-->>Bridge: {process_variables, task_results}
Bridge-->>WE: task execution result

else Gateway Node
Note over Bridge,WE: MCPFlowBridge exposes route_gateway to WorkflowEngine
WE->>Bridge: route_gateway(gateway_id, process state)
Bridge->>FA: _handle_gateway(gateway_id, process state)
FA->>DA: route(available_flows, state)
DA-->>FA: chosen flow_id
FA-->>Bridge: flow_id
Bridge-->>WE: routing decision (flow_id)

else Hook Node
Note over Bridge,WE: MCPFlowBridge exposes evaluate_hook to WorkflowEngine
WE->>Bridge: evaluate_hook(hook_id, process state)
Bridge->>FA: _handle_hook(hook, process state)
FA-->>Bridge: HookResult {action, intervention details}
Bridge-->>WE: hook intervention
end

end

rect rgb(255, 245, 225)
Note over App,WE: COMPLETION
WE-->>Bridge: (FlowState, ProcessModel)
Bridge-->>FA: FlowState
FA-->>App: FlowState
end
```

---

## MCP Service Contracts

### Services MCPFlowBridge Exposes to WorkflowEngine

| MCP Tool | Called by | Routed to | Input |
|---|---|---|---|
| `execute_task` | WorkflowEngine | FlowAgent → TaskAgent | `task_id`, `process state` |
| `route_gateway` | WorkflowEngine | FlowAgent → DecisionAgent | `gateway_id`, `process state` |
| `evaluate_hook` | WorkflowEngine | FlowAgent | `hook_id`, `process state` |

### Service WorkflowEngine Exposes to CUGA FLO

| MCP Tool | Called by | Handled by | Input |
|---|---|---|---|
| `run_process` | FlowAgent (via MCPFlowBridge) | WorkflowEngine | `process_key`, `initial_inputs` |

---

## CUGA FLO Service Results

### `execute_task` → Result to WorkflowEngine

| Field | Type | Description |
|---|---|---|
| `process_variables` | `Dict[str, Any]` | Updated process variables after output mapping |
| `task_results` | `Dict[str, Dict]` | `{task_id: {status: "completed"\|"error", output: "...", error: "..."}}` |

---

### `route_gateway` → Routing Result to WorkflowEngine

| Field | Type | Description |
|---|---|---|
| `flow_id` | `str` | The single chosen outgoing sequence flow ID (e.g., `"flow_approved"`) |

DecisionAgent resolves the routing via a two-step internal graph:
1. **eval_condition** — deterministic safe-string evaluation of `${var} op value` conditions
2. **decide** — LLM (CugaAgent) reads evaluation result + gateway policy + state and returns exactly one `flow_id`

---

### `evaluate_hook` → Interventions Suggested to WorkflowEngine

| `action` | Effect on WorkflowEngine | Additional Fields |
|---|---|---|
| `CONTINUE` | Proceed normally along the current edge | — |
| `SKIP_NODE` | Skip the immediately next node | `message` |
| `SKIP_TO` | Jump execution to a named node | `skip_to_node: str` |
| `REQUEST_USER_INPUT` | Pause process and surface prompt to user | `user_prompt: str` |
| `TERMINATE` | Halt the process immediately | `message` |
| `SWAP_NODES` | Exchange two nodes in the current graph | `swap_nodes: (node_a, node_b)` |
| `REMOVE_NODE` | Remove a node and rewire surrounding flows | `remove_node: str` |
| `ADD_NODE` | Insert a new node before target (triggers graph rebuild) | `add_node: {node_id, task_instruction}` |
147 changes: 147 additions & 0 deletions docs/diagrams/cuga-flo-lifecycle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# CUGA FLO Lifecycle — Sequence Diagram

```mermaid
sequenceDiagram
autonumber
participant Sup as Supervisor (CugaAgent)
participant Deleg as delegation.py
participant App as Application
participant SC as supervisor_config.py
participant FC as FlowConfig
participant FA as FlowAgent
participant Bridge as MCPFlowBridge
participant Reg as ProcessRegistry
participant Eng as LangGraphWorkflowEngine
participant FS as FlowState
participant TA as TaskAgent
participant DA as DecisionAgent

rect rgb(230, 240, 255)
Note over App,Reg: STARTUP
App->>App: cuga start flow_agent_inline<br/>sets DYNACONF_SUPERVISOR__CONFIG_PATH
App->>SC: load supervisor YAML
SC->>SC: finds agent entry with type: flow_agent
SC->>FC: load_flow_from_yaml(flow_config_path)
FC->>Bridge: <<create>> MCPFlowBridge(name="cuga-flo-bridge")
FC->>Reg: <<create>> ProcessRegistry(bridge)
Note right of Reg: Services: register_flow(config_path)<br/>get_bpmn_process(key), get_flow_annotations(key)<br/>list_keys()
Reg->>Bridge: register_registry(self)
Note right of Bridge: Registers MCP tools: register_flow, get_bpmn_process, get_flow_annotations<br/>Bridge holds internal Reg reference
FC->>Bridge: load_flow(config_path)
Bridge->>Reg: register_flow(config_path)
Note right of Reg: Parses YAML (tasks, gateways,<br/>hooks, policies, action_permissions)<br/>+ BPMN file via BPMNParser<br/>Stores FlowConfig + BPMNProcess in cache
FC->>FA: <<create>> FlowAgent(process_key, bridge)
FA->>Bridge: get_flow_annotations(process_key)
Bridge->>Reg: get_flow_annotations(process_key)
Reg-->>Bridge: FlowConfig
Bridge-->>FA: FlowConfig
loop per agentic task (mode: task_agent)
FA->>TA: <<create>> TaskAgent(task_id, CugaAgent(tools, system_instruction))
end
loop per decision gateway (mode: decision_agent)
FA->>DA: <<create>> DecisionAgent(gateway_id, policy, flow_decisions)
end
Note right of FA: Also builds task_instructions, task_policies<br/>hooks, action_permissions from FlowConfig
FA->>Bridge: register_flow_agent(self)
Note right of Bridge: Registers MCP tools:<br/>execute_task, route_gateway, evaluate_hook
FC->>Eng: <<create>> LangGraphWorkflowEngine(bridge)
Eng->>Bridge: register_engine(self)
Note right of Bridge: Registers MCP tool: run_process<br/>All tools backed by FastMCPTransport (in-process)
end

rect rgb(220, 235, 255)
Note over Sup,FA: SUPERVISOR DELEGATION (optional path)
Sup->>Deleg: create_agent_delegation_func(adapter, agent_name, flow_agent)
Note right of Deleg: agent_or_config is FlowAgent instance<br/>Detected via isinstance(agent_or_config, FlowAgent)
Sup->>Deleg: delegate_to_agent(task, variables)
Deleg->>FA: invoke(input_data=task, process_variables=vars_to_pass)
FA-->>Deleg: FlowState
Deleg->>Deleg: extract last message from FlowState.messages
Deleg-->>Sup: answer string
end

rect rgb(230, 255, 230)
Note over App,Eng: INVOCATION
App->>FA: invoke(input_data, process_variables)
Note right of FA: str input_data → initial_inputs["_user_message"]<br/>dict input_data → merged into initial_inputs
FA->>Bridge: run_process(process_key, initial_inputs)
Bridge->>Eng: _run_via_mcp(process_key, initial_inputs, mcp_server)
Eng->>Bridge: call_tool("get_bpmn_process", {process_key})
Bridge->>Reg: get_bpmn_process(process_key)
Reg-->>Bridge: BPMNProcess
Bridge-->>Eng: BPMNProcess dict → from_dict()
Eng->>Bridge: call_tool("get_flow_annotations", {process_key})
Bridge->>Reg: get_flow_annotations(process_key)
Reg-->>Bridge: FlowConfig.to_engine_config()
Bridge-->>Eng: {agentic_task_ids, decision_gateway_ids, task_instructions, hooks,<br/>flow_conditions, tool_tasks, action_permissions}
Note right of Eng: Builds _ControlOverlay with<br/>MCP-backed handlers for each<br/>task / gateway / hook
Eng->>FS: FlowState(process_id, process_variables)
Note right of Eng: Engine owns FlowState<br/>Compiles + begins StateGraph execution
end

rect rgb(255, 245, 220)
Note over Eng,TA: ENGINE EXECUTION — Agentic Task Node
Eng->>Eng: _create_task_node(task_id)
Note right of Eng: Builds ControlPointContext:<br/>current_state — engine-held FS<br/>execution_history — engine-held path<br/>process_model_summary — inspect_model()<br/>task_instruction — static config[task_id]
Eng->>Bridge: call_tool("execute_task", {task_id, ctx.to_dict()})
Bridge->>FA: _handle_task(task_id, ctx)
Note right of FA: WHO: self.task_agents[task_id]<br/>HOW: self.task_policies[task_id]<br/>WHAT: ctx.task_instruction (engine disclosed)
FA->>FA: _build_task_input(task_id, ctx)
Note right of FA: combines task_instruction + policy +<br/>user_msg + process_variables + task_results
FA->>TA: execute(ctx.current_state, task_input)
Note right of TA: _process_output applies output_mapping:<br/>parses JSON from LLM output<br/>calls state.set_process_variable(var, val)<br/>for each output_key → process_var mapping
TA-->>FA: task result dict (process_variables already updated in-place)
FA-->>Bridge: partial dict {execution_path, task_results, process_variables}
Bridge-->>Eng: result dict
Eng->>FS: merge partial dict via reducers
end

rect rgb(255, 235, 235)
Note over Eng,DA: ENGINE EXECUTION — Gateway Node (two paths)
Eng->>Eng: _create_gateway_node(gateway_id)
Note right of Eng: Builds ControlPointContext:<br/>available_flows — outgoing BPMNFlows with<br/>flow_conditions overlaid from static config<br/>current_state — engine-held FS

alt mode: decision_agent (overlay.gateway_handlers present)
Eng->>Bridge: call_tool("route_gateway", {gateway_id, ctx.to_dict()})
Bridge->>FA: _handle_gateway(gateway_id, ctx)
FA->>DA: route(ctx.available_flows, ctx.current_state)
Note right of DA: eval_condition → decide StateGraph<br/>LLM reads condition result + policy<br/>returns chosen flow_id
DA-->>FA: chosen flow_id
FA-->>Bridge: flow_id string
Bridge-->>Eng: flow_id
else mode: tool (merge / pass-through, no gateway handler)
Eng->>Eng: _tool_route_gateway(gateway_id, flows, state, overlay)
Note right of Eng: eval_condition on each outgoing flow<br/>Returns first matched flow_id<br/>No MCP round-trip — pure engine logic
end

Eng->>FS: gateway_decisions[gw_id] = flow_id
Note right of Eng: Routes conditional edge
end

rect rgb(245, 230, 255)
Note over Eng,FA: ENGINE EXECUTION — Hook Node (PRE_EDGE)
Eng->>Eng: _create_hook_node(edge_id, hook, normal_target, process, overlay)
Note right of Eng: Builds ControlPointContext:<br/>edge_id — intercepted flow ID<br/>current_state — engine-held FS<br/>execution_history — engine-held path<br/>(no task_instruction)
Note right of Eng: If hook.condition set and not met → skip,<br/>Command(goto=normal_target)
Eng->>Bridge: call_tool("evaluate_hook", {hook_id, ctx.to_dict()})
Bridge->>FA: _handle_hook(hook, ctx)
Note right of FA: Checks hook.condition against state<br/>If policy: _llm_hook_decision(hook, ctx)<br/> → LLM reads policy + process_variables<br/> → returns {action, target_node, state_updates}<br/>If handler: hook.handler(state)<br/>Returns HookResult
FA-->>Bridge: HookResult dict {action, skip_to_node, state_updates, ...}
Bridge-->>Eng: HookResult dict
Note right of Eng: Enforces action_permissions<br/>Applies state_updates to process_variables<br/>SKIP_TO → Command(goto=skip_to_node)<br/>CONTINUE → Command(goto=normal_target)
end

rect rgb(240, 255, 240)
Note over Eng,App: COMPLETION
Eng-->>Bridge: (FlowState, BPMNProcess)
Bridge-->>FA: {state: FlowState.model_dump(), bpmn: bpmn.to_dict()}
FA->>FS: FlowState.model_validate(result["state"])
Note right of FA: FA does NOT store FlowState<br/>Ready for next invoke() call
alt not is_halted
FA->>FS: mark_complete()
end
FA->>FA: _build_completion_message(final_state, BPMNProcess.from_dict(result["bpmn"]))
FA->>FS: messages.append(summary)
FA-->>App: FlowState
end
```
Loading