38 KiB
tinyflows — Completion & Integration Plan (POA)
Status: proposed · Date: 2026-07-04 Scope: finish integrating the vendored
vendor/tinyflows/workflow engine (our n8n/Zapier module) into the Rust core and the desktop UI, and close the feature gaps that separate it from a real n8n-class product. Reference product: n8n — item-based data flow,=-expressions, node catalog, editable canvas, credentials, webhook/schedule triggers, executions view.
1. Where we are today
The integration is not greenfield. Three layers already exist; each is at a different maturity level.
1.1 Engine (vendor/tinyflows/, v0.3.0 — mostly done)
Host-agnostic library crate on the tinyagents state-graph runtime. Pipeline: WorkflowGraph → migrate → validate → compile → engine::run.
| Area | State |
|---|---|
| Node kinds (12) | ✅ trigger, agent, tool_call, http_request, code, condition, switch, merge, split_out, transform, output_parser, sub_workflow |
| Routing | ✅ linear, conditional (ports), parallel fan-out, merge fan-in barrier |
| Data flow | ✅ n8n-style item arrays { json, binary?, paired_item? }; =-expressions with full jq (jaq-core) already wired in src/expr.rs |
| Error handling | ✅ per-node on_error (stop/continue/route), retry with fixed/exponential backoff, node_timeout_secs |
| HITL | ✅ requires_approval → interrupt; in-process (run_resumable) and cross-process durable resume (resume_with_checkpointer(thread_id)) |
| Observability | ✅ RunObserver trait (on_run_start/on_step_finish/on_run_finish), tracing spans, journaled variants (GraphEventJournal) for Langfuse |
| Persistence | By design none — host injects Checkpointer, StateStore, and persists Run/ExecutionStep |
| Capabilities | ✅ host-injected traits: LlmProvider, ToolInvoker, HttpClient, CodeRunner, StateStore; opaque connection_ref (host resolves secrets) |
| Versioning | ✅ schema_version (graph) + per-node type_version, migrate() pre-parse |
| Triggers | ⚠️ declarative only — manual, schedule, webhook, app_event, form, execute_by_workflow, chat_message, evaluation, system; the host must fire them |
Engine-side gaps (see Phase 7): agent node sub-ports (chat_model/memory/tool/output_parser) stubbed; output_parser is identity passthrough; README/Roadmap lag the code (claim jq and retry backoff are pending when both ship).
1.2 Rust core seam (src/openhuman/flows/ + src/openhuman/tinyflows/ — implemented, with holes)
- Domain
flows::(~3,700 lines + tests):types.rs(FlowwrapsWorkflowGraph+enabled/require_approval/last_status;FlowRun,FlowRunStep,FlowRunTrigger::{Rpc,Schedule,AppEvent,Resume}),store.rs(SQLite incl.flow_statekv),ops.rs(validate/migrate + full run/resume underTrustedAutomation → Workfloworigin, 600 s timeout),schemas.rs,tools.rs(ProposeWorkflowTool— validate-only, never persists),bus.rs(FlowTriggerSubscriber). - RPC surface (10 methods, wired in
src/core/all.rs):openhuman.flows_{create,get,list,update,delete,set_enabled,run,resume,list_runs,get_run}. - Capability seam
src/openhuman/tinyflows/:caps.rs(LLM/Composio-tools/HTTP/code/state adapters),observability.rs(currentlyNoopObserver),langfuse_export.rs(post-run trace export). - Schedule triggers work end-to-end:
flows::ops::bind_schedule_triggerregisters a cronJobType::Flow→ scheduler publishesDomainEvent::FlowScheduleTick→FlowTriggerSubscriberruns the flow. - Composio
app_eventtriggers also work end-to-end:flows/bus.rs::handle_app_eventmatchesDomainEvent::ComposioTriggerReceived { toolkit, trigger }against enabledapp_eventflows (case-insensitive toolkit/slug match, per-flow concurrency guard) and runs them underFlowRunTrigger::AppEvent. Since Composio triggers are delivered by the platform, this is our webhook story for third-party apps — no tunnel needed.
Known core gaps (each becomes a workstream below):
| # | Gap | Evidence |
|---|---|---|
| G1 | Raw webhook triggers unwired (mitigated: Composio app_event triggers already cover third-party apps) — enabling a webhook-trigger flow only logs a warning; bus.rs observes WebhookIncomingRequest but explicitly does not dispatch; Composio trigger subscriptions are not auto-provisioned on enable |
flows/ops.rs:298 (log_webhook_trigger_deferred), flows/bus.rs:232-242 |
| G2 | No live run observer — NoopObserver; FlowRunSteps reconstructed post-hoc from final state, no per-step timing/attempts, nothing streamed while running |
flows/types.rs:76, flows/ops.rs:781-783 (TODO(0.3)) |
| G3 | Credential / connected-account resolution stubbed — Composio nodes fall back to the ambient signed-in account; toolkit allow-listing hard-rejects real toolkits; HTTP credential resolution unimplemented (connection_ref is accepted but unresolvable) |
tinyflows/caps.rs:193,235-261,330,376,408 |
| G4 | No cancel/deny — a dismissed approval leaves the run parked pending_approval forever; no flows_cancel/flows_deny RPC |
UI comment in FlowApprovalCard.tsx |
| G5 | No JSON-RPC E2E coverage — zero openhuman.flows_* calls in tests/json_rpc_e2e.rs (unit tests only) |
grep of tests/*.rs |
| G6 | Unfired trigger kinds: chat_message, form, execute_by_workflow (as a trigger), evaluation, system have no host dispatcher |
flows/bus.rs |
1.3 Frontend (app/src/ — reachable, read-only)
Shipped: /flows nav tab (FlowsPage list: enable toggle, Run, last status), /flows/:id read-only canvas (@xyflow/react v12, custom nodes, minimap), FlowRunsDrawer + FlowRunInspectorDrawer (2 s polling via useFlowRunPoller), chat WorkflowProposalCard (agent propose_workflow → user "Save & enable" is the only creation path), FlowApprovalCard HITL notifications. All components have co-located Vitest coverage; i18n namespaces flows.*/flowRuns.*/notifications.flow.* exist across locales.
Known UI gaps:
| # | Gap | Evidence |
|---|---|---|
| U1 | No canvas editing — nodesDraggable/Connectable/elementsSelectable: false; xyflowToWorkflowGraph in graphAdapter.ts is dead code awaiting the editor ("B5b.2+") |
components/flows/canvas/FlowCanvas.tsx |
| U2 | No node config panel — clicking a node does nothing; config shown only as truncated hint chips |
— |
| U3 | No authoring entry — "New workflow" navigates to /chat with a TODO; empty-state copy promises canvas creation that doesn't exist |
pages/FlowsPage.tsx |
| U4 | No trigger-config UI, no credentials picker, no template gallery, no import/export | — |
| U5 | Approval "Dismiss" is client-only (blocked on G4) | FlowApprovalCard.tsx |
| U6 | Run progress is poll-only; no socket push (blocked on G2) | hooks/useFlowRunPoller.ts |
1.4 Disambiguation (do not conflate)
The repo has three "workflow" systems. This plan touches only the first:
flows::/openhuman.flows_*— tinyflows typed graphs (this plan).workflows::/openhuman.workflows_*— WORKFLOW.md/SKILL.md bundle discovery/install (separate product surface under/skills). Slated for decommission — it is essentially the skills feature wearing the "workflows" name; see Phase 8. Retiring it frees the "Workflows" branding for tinyflows (the/flowsnav tab already reads "Workflows").rhai::— Rhai.ragshlanguage workflows (docs/plans/rlm-workflows/), positioned ingitbooks/features/orchestration.mdas the next layer on the same substrate. tinyflows remains the shipping visual/typed product; Rhai workflows do not replace it.
2. Gap analysis vs n8n / Zapier
What an n8n user would expect, mapped to our state:
| n8n capability | tinyflows/OpenHuman today | Covered by |
|---|---|---|
| Editable node canvas (drag, connect, add/delete) | Read-only viewer | Phase 3 |
| Node config side-panel with schema-driven forms | Missing | Phase 3 |
| Webhook trigger with live URL | Declared but never fired | Phase 1 |
| Cron/interval trigger | ✅ shipped | — |
| App-event trigger (≈ Zapier polling/instant triggers) | Wired via Composio ComposioTriggerReceived, but account resolution stubbed |
Phase 1 (G3) |
| Credentials store + per-node credential picker | connection_ref plumbing exists; resolution unimplemented; no UI |
Phase 2 |
| Executions list + live run view | List + inspector exist; polling, post-hoc steps only | Phase 1–2 |
| Cancel a running/paused execution | Missing | Phase 1 |
Expressions (={{ }} in n8n) |
✅ =-prefix + full jq |
— |
| Error workflow / retry per node | ✅ on_error/retry/backoff | — |
| Sub-workflows | ✅ sub_workflow node (inline graph); by-id reference missing |
Phase 4 |
| Templates gallery | Missing | Phase 4 |
| Import/export workflow JSON, n8n import | Missing (feasible: model is deliberately n8n-shaped) | Phase 4 |
| Partial execution / pin data / step-through debug | Missing | Phase 5 (stretch) |
| Waiting/Wait node, human approval | ✅ HITL approval + durable resume | — |
| AI Agent node with attached tools/memory | agent node = bare LLM call; sub-ports stubbed |
Phase 6 |
| Versioning of workflow definitions | ✅ schema_version + type_version + migrate | — |
| Multi-user sharing/RBAC | Out of scope (desktop, single-user + team domain later) | — |
Our structural advantages to preserve: the agent can author workflows conversationally (propose_workflow → proposal card → single human "Save & enable" persistence gate), everything runs under the security policy/approval-gate substrate, and secrets never enter the engine (opaque connection_ref).
3. Plan of action
Ordering rationale: backend correctness first (triggers, cancellation, live observation, credentials) because every UI phase consumes those RPCs; the editable canvas is the largest UI lift and is independent of trigger work, so it proceeds in parallel from Phase 3.
Phase 0 — Substrate hygiene: tinyagents pin & tags
Every workflow is, at run time, a unique tinyagents state graph: compile() wraps the WorkflowGraph, and the engine lowers it per run into a tinyagents graph (nodes → graph nodes, output ports → conditional edges, fan-in → waiting edges); run state (MergeReducer over a single JSON value), durable checkpointing, HITL interrupts, and the event journal are all tinyagents primitives, keyed per run by thread_id/run_id. So the health of the tinyagents pin is the health of the whole feature.
Audit (2026-07-04):
- ✅ Single unified copy — both Cargo worlds (
Cargo.toml:355,app/src-tauri/Cargo.toml:214) patchtinyagentstovendor/tinyagents; exactly onetinyagents 1.5.0in both lockfiles. tinyflows'tinyagents = "1.2"requirement unifies onto the same copy (semver-compatible). No duplication, no type-identity risk. - ⚠️ Not on a tag — the submodule is pinned at
df391c4=v1.5.0-13-gdf391c4: 13 untagged commits past v1.5.0 (REPL host-embedding / cancel work taken early for the Rhai workflow feature). The crate still self-reports1.5.0, so the version string understates the vendored code. - ⚠️ Two minor versions behind — upstream is at v1.7.1 (60 commits past v1.5.0). The 13 early-adopted commits all landed upstream (via PR #19 etc.;
ReplSession::set_cancel_flagverified present in v1.7.1), so retagging loses nothing. - ⚠️ Same pattern on tinyflows itself: submodule at
v0.3.0-1-g438f8fc(one commit past tag).
Work items:
- Bump
vendor/tinyagentssubmodule to the v1.7.1 tag; bump root requirementtinyagents = { version = "1.7", features = ["sqlite", "repl"] }(verify both features still exist in 1.7);cargo update -p tinyagentsin both lockfiles. - Review the v1.5→v1.7 changelog for API breaks in the seams we touch:
Checkpointer/DurabilityMode,GraphEventJournal/GraphObservation, interrupt/resume semantics (used byflows::ops+src/openhuman/tinyagents/+ Rhai workflows). - Retag
vendor/tinyflowson a proper release (v0.3.1) whoseCargo.tomlrequirestinyagents = "1.7"so the version story is coherent end-to-end. - Gate with
cargo checkboth worlds,pnpm test:rust, and the flows/Rhai unit suites; adopt a standing rule: vendored tiny* submodules pin release tags, never floating commits (early-adopting an upstream PR requires a pre-release tag).
Phase 1 — Backend completion (triggers, lifecycle, observability)
1a. External event triggers — Composio-first (G1).
Product decision: Composio triggers are the webhook story. app_event dispatch already works end-to-end (see §1.2) and the platform handles inbound delivery, auth, and NAT traversal — so we do not build our own tunnel/webhook infrastructure for third-party app events. Remaining work is lifecycle and coverage, not plumbing:
- Trigger subscription lifecycle: enabling an
app_eventflow should ensure the corresponding Composio trigger subscription exists upstream (create onset_enabled(true)via thecomposiodomain, tear down on disable/delete when no other flow uses it) instead of assuming the user pre-configured it in Composio. Reconcile at boot likebind_schedule_triggerdoes. - Payload hygiene: trigger payloads are untrusted input — route through
prompt_injectionscreening before anyagentnode consumes them (applies to all external-event runs). - Trigger catalog surfacing: expose the Composio trigger catalog (toolkit → available trigger slugs + payload schemas) over RPC so the UI (Phase 3 trigger config) and the builder agent (Phase 5
search_tool_catalog) offer real, connectable events instead of free-text slugs. - Raw
webhooktrigger kind → demoted: for arbitrary custom HTTP callers not covered by a Composio toolkit, keep thewebhookkind but defer implementation (generic inbound viawebhooks::ops/create_tunnelstays a backlog item). Until then, validation must warn loudly at save/enable time that awebhook-trigger flow will not fire, instead of today's silent log line (flows/ops.rs:298).
1b. Run lifecycle: cancel + deny (G4).
- New RPCs
openhuman.flows_cancel_run(run_id)(terminalcancelledstatus; abort the tokio task / drop the checkpointed thread) and deny semantics on resume:flows_resume(id, thread_id, approvals, rejections)→ rejected node routes to itserrorport or fails the run. - Sweep: TTL for parked
pending_approvalruns (align with the 10-min approval-gate TTL, configurable per flow).
1c. Live run observation (G2).
- Implement a real
RunObserverinsrc/openhuman/tinyflows/observability.rs:on_step_finish→ persistFlowRunStepincrementally (timing, attempt count, status, output) viaflows::store, and publish a newDomainEvent::FlowRunProgress { run_id, node_id, status }. - Bridge to the frontend socket (
socketdomain) so the inspector can subscribe instead of polling (UI lands in Phase 2/3; keep polling as fallback). - Wire the journaled run variants so Langfuse export happens per-step, not only post-run.
1d. Remaining trigger kinds (G6).
Full taxonomy, the generic event-dispatcher design, and the cron → workflows migration live in the companion doc triggers.md. Summary:
form+ one-shot/interval schedule sugar first (cheap wins).- A trigger catalog (curated allowlist of
DomainEvents with stable payload schemas + jq filters) feeding one genericsystem-kind dispatcher inFlowTriggerSubscriber— instead of a bespoke handler per event. Guardrails: loop prevention via run provenance depth, per-flow rate limits, payload hygiene/risk classes. chat_messagefrom the channels pipeline (untrusted-input guardrails required first).- Cron unification: legacy
JobType::Shell/Agentcron jobs become one-node scheduled flows (dual-write bridge, then backfill migration); cron remains only as the internal tick engine. "Everything automated is a workflow." - Until a kind's dispatcher ships, validation warns loudly at save/enable instead of silently never firing.
1e. E2E tests (G5).
- Extend
tests/json_rpc_e2e.rs(+scripts/test-rust-with-mock.sh): full lifecycle round-trip (create → run → steps → resume → cancel → delete), schedule-tick dispatch, webhook dispatch, approval park/deny. This is the merge gate for 1a–1d.
Deliverable: every trigger kind either fires or loudly warns; runs can be cancelled/denied; run steps stream.
Phase 2 — Credentials & connections (G3)
The connection_ref seam exists end-to-end but resolves nothing. This phase makes integration nodes real.
- Composio connected accounts: resolve
connection_ref→ specific Composio connected account (today: ambient-account fallback). Fix toolkit allow-listing incaps.rs:193so real toolkits pass policy instead of being hard-rejected. Surface account choice in the node config (connection_ref= connected-account id). - HTTP credentials: back
connection_refforhttp_requestnodes with the existingcredentialsdomain (header/bearer/basic templates stored encrypted; injected server-side inOpenHumanHttp::request— never returned to the UI or the engine). - RPCs:
flows_list_connections()(aggregate Composio connected accounts + stored HTTP credentials, ids + display names only) for the UI picker. - Policy:
codeandhttp_requestnode execution must respect the[autonomy]tier /classify_command-equivalent gating (network class); document the matrix inflows/ops.rs.
Deliverable: an http_request or Composio tool_call node can be pointed at a chosen account/credential by reference, with secrets never leaving the core.
Phase 3 — Editable canvas (U1–U2) — the n8n builder
Largest UI phase; runs in parallel with Phase 2 once Phase 1c's RPCs exist.
- 3a. Edit mode in
FlowCanvas: flip the readonly defaults behind aneditableprop; enable drag (persistposition), connect (port-aware: derive valid source/target handles from node kind — reusegraphAdapterport logic), delete nodes/edges, and a node palette (12 kinds with the existing emoji/accent metadata). Wire the already-writtenxyflowToWorkflowGraphas the save path →flows_update. - 3b. Node config panel: right-hand drawer on node select. v1 pragmatic approach: per-kind form components for the high-traffic kinds (
triggerschedule/webhook config,http_requestmethod/url/headers/body,agentprompt/model,tool_callslug/args,condition/switchexpression,transformset-map,codeeditor with language toggle) + a raw-JSON escape hatch for the rest.=-expression fields get a monospace input with an "expression" affordance (full expression-editor with live preview is a stretch goal — needs aflows_eval_exprRPC against sample run data). - 3c. Validation UX: new RPC
openhuman.flows_validate(graph)(thin wrapper overops::validate_and_migrate_graph, same pathpropose_workflowuses) → inline canvas errors (missing trigger, cycle, invalid config on node X) before save. - 3d. Draft/dirty state: local draft in component state; explicit Save; unsaved-changes guard. No autosave in v1 (a saved+enabled flow is live — accidental saves fire schedules).
- 3e. Live run overlay: subscribe to
FlowRunProgresssocket events; animate node status on the canvas during a run (n8n's signature interaction) and in the inspector.
Deliverable: create/edit a workflow entirely on the canvas; watch it execute live.
Phase 4 — Authoring entry points, templates, interop (U3–U4)
- 4a. "New workflow": replace the
/chatTODO with a chooser — Start from scratch (blank canvas with a trigger node), Describe it (interim: prefill chat composer →propose_workflow; superseded by the Phase 5 in-place prompt bar), From template. - 4b. Sub-workflow by id: extend
sub_workflow/execute_by_workflowto reference a savedflow_id(engine currently only inlines a child graph) — engine change upstreamed tovendor/tinyflows+ host resolver incaps/ops. - 4c. Templates: ship 5–10 curated
WorkflowGraphJSONs (bundled resources, like agent prompts): e.g. "Daily digest to channel", "Webhook → agent triage → notify", "Scheduled scrape → transform → memory". Gallery UI on FlowsPage empty state. - 4d. Import/export: export flow JSON; import with
migrate()+ validate. n8n importer (host-side, best-effort): map n8n workflow JSON →WorkflowGraphfor the overlapping vocabulary (IF→condition, Switch, Merge, SplitOut, HTTP Request, Code, Schedule/Webhook triggers;={{...}}→=jq where trivially translatable); unmapped nodes land as annotated placeholder nodes rather than failing the import. - 4e. Proposal card upgrade: "Open in canvas" action on
WorkflowProposalCard(review/edit before Save & enable) — keeps the single persistence gate.
Phase 5 — Prompt-first authoring: the Workflow Builder agent
The product stance ("the agent builds it, you approve it") gets a first-class surface: users prompt from the Workflows UI itself — not by wandering into /chat — and a dedicated, tool-scoped agent designs and iterates on the graph in place. This is the differentiator over n8n, so it deserves its own phase rather than being a bullet under 4a.
5a. A dedicated workflow-builder agent definition.
The harness already supports exactly this shape: AgentDefinition (src/openhuman/agent/harness/definition.rs) with ToolScope::Named, registered as a builtin in harness/builtin_definitions.rs and overridable by user TOML via definition_loader.rs.
- New builtin definition
workflow-builder(Worker tier): system prompt specialized for workflow design — knows the 12 node kinds,=/jq expression semantics, port/edge rules, trigger kinds and which ones are live, error-handling config (on_error/retry), and the "propose, never persist" invariant. Prompt ships insrc/openhuman/agent/prompts/like the other bundled prompts. ToolScope::Named— deliberately narrow toolset (see 5b): no shell, no file writes, no channel sends.external_effect = falseend-to-end; the only side effects it can cause are validated proposals.- Reachable two ways: (1) directly from the Flows UI prompt surface (5c), spawned with the flow/draft as context; (2) by delegation from the main agent via the existing delegation tools (
agent/tools/delegate_to_personality.rs, archetype/skill delegation inagent_orchestration::tools) so "set up a workflow that…" in normal chat routes to the specialist automatically.
5b. The builder's tool belt (new tools live in flows/tools.rs per the tool-ownership rule, registered in tools/ops.rs):
| Tool | Status | Purpose |
|---|---|---|
propose_workflow |
✅ exists | Validate a full graph + emit proposal payload (unchanged invariant: never persists) |
revise_workflow |
new | Take the current draft graph + an instruction diff → emit an updated proposal; enables iterative refinement instead of regenerate-from-scratch |
list_workflows / get_workflow |
new (read-only) | Inspect existing flows so the agent can reference, clone, or avoid duplicating them |
get_workflow_run |
new (read-only) | Read a failed run's steps so the agent can debug/repair a workflow from an error report |
list_flow_connections |
new | Enumerate Composio connected accounts + stored HTTP credentials (ids/names only — Phase 2's RPC surfaced as a tool) so generated nodes carry valid connection_refs |
search_tool_catalog |
new | Search the Composio/tools registry for real tool slugs, so tool_call nodes are grounded in tools that actually exist (today the agent can hallucinate slugs) |
dry_run_workflow |
new | Execute the draft graph against mock/sandboxed capabilities (tinyflows mock feature or capped real caps with requires_approval forced on) and return step results — lets the agent self-verify before proposing |
All read-only tools return PermissionLevel::None; dry_run_workflow is gated by autonomy tier since code/http_request nodes could execute.
5c. Prompt surface in the Flows UI.
- FlowsPage prompt bar: a "Describe a workflow…" composer at the top of
/flows(and as the empty-state hero). Submitting spawns aworkflow-builderturn in a dedicated thread; the resulting proposal renders inline (reuseWorkflowProposalCard) with Open in canvas and Save & enable. - Canvas copilot panel: on
/flows/:id(and on drafts), a side panel chat bound to the same agent with the current graph injected as context. Each agent proposal updates a draft overlay on the canvas (diff-style: added nodes highlighted, removed ones ghosted) — accept/reject applies it to the local draft from Phase 3d. This isrevise_workflowin a loop: "add a Slack notification on failure", "make the schedule weekdays only", "split this into a sub-workflow". - Repair entry point: from a failed run in
FlowRunInspectorDrawer, "Fix with agent" opens the copilot with the run's failing step context preloaded (get_workflow_run). - Plumbing reuses the existing chat runtime (
ChatRuntimeProvideralready parsespropose_workflowoutputs intopendingWorkflowProposalsByThread); the new work is thread scoping per draft/flow, the canvas diff overlay, and routing turns to theworkflow-builderdefinition instead of the main agent.
5d. Invariants (carry over from the current design, enforced in review):
- The agent never persists or enables a flow —
flows_create/flows_update/set_enabledremain UI-only actions behind an explicit user click. - Proposals are always re-validated server-side at save time (
flows_validatepath), never trusted from the client. dry_run_workflowoutput is labeled as sandbox output in the UI so users don't mistake it for a live run.
Deliverable: a user types "every Monday, summarize my unread Slack messages and email me" into the Flows page, watches the graph appear on the canvas, iterates in plain language, then clicks Save & enable.
Phase 6 — Polish & debug tooling (stretch)
- Partial execution ("run from this node" with pinned upstream data) — needs engine support for seeding node state; scope with tinyflows maintainers.
- Run diff/inspector niceties: per-item data browser (n8n's table/JSON toggle), input↔output pairing via
paired_item. flows_duplicate, run retention/pruning policy, per-flow run-history limits.- Desktop E2E (WDIO) spec: create → run → inspect happy path.
Phase 7 — Engine (vendor/tinyflows) upstream work
Tracked separately since it's a submodule with its own release cadence (host pins 0.3, patched to path):
agentnode sub-ports (chat_model/memory/tool/output_parser wiring) — unlocks n8n-style "AI Agent with tools" composition.output_parser: schema validation + LLM auto-fix (currently identity).- Sub-workflow by reference (4b) — config
workflow_idalternative to inlineworkflow. - Docs truth-up: README/Roadmap still claim jq and retry backoff are pending; both are implemented (
src/expr.rsroutes to jaq;engine.rshas fixed/exponential backoff +node_timeout_secs). - Optional: cancellation token support in
engine::run(cleaner than task-abort for 1b).
Phase 8 — Decommission the legacy workflows:: bundle domain
The workflows:: domain (WORKFLOW.md/SKILL.md bundle discovery/install, RPC openhuman.workflows_*) predates tinyflows and is functionally the skills feature under a different name. Keeping two things called "workflows" confuses users, agents, and contributors alike. Plan: fold what's unique into skills, delete the rest, and hand the name to tinyflows.
- Audit consumers first:
agent/tools/run_workflow.rs(agent tool that runs WORKFLOW.md bundles — decide: retire, or repoint toflows_run/skills), the/skillsUI surfaces (WorkflowsTab,CreateWorkflowForm,WorkflowRunnerBody,WorkflowNew.tsx,WorkflowsRun.tsx,DevWorkflowPanel,workflowsApi.ts),about_app, gitbooks, and the Rhai workflow plan's references torun_workflowas a composition surface. - Migrate: bundle discovery/install semantics that skills doesn't already cover move into the
skillsdomain (it is metadata-only post-QuickJS-removal, so this is mostly file-format and registry work). - Deprecate then delete: mark
openhuman.workflows_*deprecated for one release (RPC responses carry a deprecation notice), then removesrc/openhuman/workflows/, its controllers fromsrc/core/all.rs, the frontend clients/pages, and the/workflows/new//workflows/runroutes (bare/workflowsalready redirects to/settings/automations). - Not in scope:
openhuman.workflow_run_*(agent_orchestration's declarative run ledger) is a different system and untouched here — though its name should also be revisited once "Workflows" ≡ tinyflows. - Naming end-state: one user-facing concept — Workflows = tinyflows graphs at
/flows; skills are skills.
4. Missing-features summary (checklist)
Substrate: ☐ tinyagents submodule → v1.7.1 tag (+ root req bump, both lockfiles) · ☐ v1.5→v1.7 API-break review · ☐ tinyflows retag (v0.3.1, tinyagents = "1.7") · ☐ tags-only submodule policy.
Backend: ☐ Composio trigger-subscription lifecycle (auto-provision on enable) · ☐ trigger catalog RPC · ☐ loud validation warning for unfired trigger kinds · ☐ raw-webhook dispatch (deferred backlog) · ☐ flows_cancel_run · ☐ resume-with-rejection/deny · ☐ live RunObserver + incremental step persistence · ☐ FlowRunProgress socket events · ☐ flows_validate RPC · ☐ flows_list_connections · ☐ Composio connected-account resolution · ☐ HTTP credential resolution · ☐ toolkit allow-list fix · ☐ chat_message trigger dispatch · ☐ sub-workflow by id · ☐ parked-run TTL sweep · ☐ JSON-RPC E2E suite.
Frontend: ☐ editable canvas (drag/connect/palette/delete) · ☐ node config panels · ☐ trigger config UI (cron builder, webhook URL display, app-event picker) · ☐ credentials picker · ☐ new-workflow chooser · ☐ template gallery · ☐ import/export + n8n import · ☐ live canvas run overlay (socket) · ☐ approval deny (real) · ☐ "Open in canvas" from proposal card · ☐ WDIO E2E spec.
Agent authoring: ☐ workflow-builder builtin AgentDefinition + specialized prompt · ☐ revise_workflow tool · ☐ read-only list_workflows/get_workflow/get_workflow_run tools · ☐ list_flow_connections tool · ☐ search_tool_catalog tool · ☐ dry_run_workflow (sandboxed) · ☐ delegation routing from main agent · ☐ FlowsPage prompt bar · ☐ canvas copilot panel with draft diff overlay · ☐ "Fix with agent" from failed runs.
Engine (upstream): ☐ agent sub-ports · ☐ output_parser validation · ☐ workflow_id sub-workflows · ☐ docs truth-up · ☐ cancellation.
Decommission: ☐ run_workflow tool disposition · ☐ bundle semantics folded into skills · ☐ workflows_* RPC deprecation release · ☐ delete src/openhuman/workflows/ + frontend pages/clients · ☐ docs/about_app rename sweep.
5. Sequencing, estimates, risks
Phase 0 (tinyagents pin) ██ ~2–3 d do first; touches both lockfiles
Phase 1 (backend) ██████ ~2–3 wk unblocks everything
Phase 2 (credentials) ████ ~1–2 wk parallel w/ Phase 3
Phase 3 (canvas) ████████ ~3–4 wk largest UI lift
Phase 4 (authoring) ████ ~2 wk
Phase 5 (builder agent) ██████ ~2–3 wk needs Phase 3 canvas + Phase 2 connections
Phase 6 (polish) ██ opportunistic
Phase 7 (engine) ── continuous, PR'd to vendor submodule ──
Phase 8 (decommission) ── independent; audit early, delete after a deprecation release ──
Risks / decisions to confirm:
- External-event delivery model — decided: Composio triggers are the webhook story (platform handles delivery/auth/NAT). Raw custom webhooks (own tunnel via
webhooks::ops::create_tunnel) stay deferred; revisit only if users need non-Composio HTTP callers. - Prompt-injection surface — webhook/app_event payloads feeding
agentnodes are untrusted; must go through theprompt_injectiondomain. Non-negotiable before G1 ships. - Editable canvas vs. agent-first authoring — product stance so far is "the agent builds it, you approve it" (
gitbooks/features/workflows.md). Phase 5 makes that stance first-class in the Flows UI itself (prompt bar + canvas copilot); the Phase 3 hand-editing canvas is the escape hatch, not the headline. Keep the prompt-first path primary in onboarding copy so the two don't compete. - GPL-3.0 licensing of
vendor/tinyflows— already vendored/linked; re-confirm distribution posture before expanding surface (flag for legal, not an engineering blocker). - Submodule cadence — engine changes (Phase 7) must land in the tinyflows repo and re-vendor; keep host-side workarounds (e.g. by-id sub-workflow resolution in caps) so UI phases aren't blocked on vendor releases.
Per-repo conventions that apply to all phases: new RPCs go through domain schemas.rs + controller registry (no dispatch.rs branches); ≥80 % diff coverage gate; verbose [flows]-prefixed debug logging on every new path; i18n keys added to en.ts and all 13 locales; update src/openhuman/about_app/ and gitbooks/features/workflows.md as features land.