Skip to content
wippyaiPublic

About

Agentic workflow framework: DAG, agentic nodes, signals, parallel, dynamic branching

Resources

Code of conduct

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Wippy logo

Dataflow

Latest Release License Documentation

Agent route pinning

Agent nodes retain a successful fallback route across iterations and recovery. This requires Agent 0.5.5 or later. A committed agent or model change clears the previous route; recovery restores it only for the same configured agent and model.

Typed storage inspection

userspace.dataflow:inspection is a read-only library for trusted owners that inspect durable flows and external signal waits. Declare it as a library import. inspection.flow(dataflow_id) returns the flow's ID, actor ID, opaque execution context, status and decoded metadata. inspection.pending_signal(dataflow_id, signal_id, node_type) returns the pending node and yield IDs for a node type that implements the signal-wait protocol. A declaration without a committed yield is absent; a committed result closes the current episode even while the node is still active. Only the latest UUID-v7 yield episode for each node is inspected, matching recovery's ordering. Older results cannot close a newer episode.

These operations retain the caller's database authority. They do not authorize the caller, reconstruct an identity, reserve a wait, or execute a workflow. The owning application must verify the returned flow's actor and terminal status before performing an external action. Use the arm's idempotency key for delivery. The signal query filters by flow, node type, active node status and yield record type rather than loading the entire flow's event history.

Errors are native: confirmed absence has kind NOT_FOUND with code DATAFLOW_FLOW_NOT_FOUND or DATAFLOW_SIGNAL_NOT_FOUND. Invalid stored metadata or wait state has kind INTERNAL, is not retryable, and uses DATAFLOW_FLOW_METADATA_INVALID or DATAFLOW_SIGNAL_STATE_INVALID. Database failures preserve their native kind, retryability and detail fields; a database-resource NOT_FOUND is not a missing-flow proof. Legacy empty metadata encodings remain empty metadata. Existing repository/client methods retain their behavior.

Durable external waits

Nodes that start external work and then wait for a signal use the declarative park contract:

local result, err = n:park({
    wait_for_signal = true,
    signal_id = stable_signal_id,
    arm = {
        ref = "component.namespace:arm_function",
        args = { correlation_id = stable_signal_id },
    },
})

park persists the node yield before the orchestrator invokes arm.ref. The arm runs in an isolated function process under the workflow's recovered actor and scope; arguments are snapshotted as bounded plain data (scalars and tables with string or positive-integer keys) before the durable commit. Cycles, runtime values, excessive nesting, and oversized payloads fail with PARK_ARM_INVALID; metatables and later caller mutations cannot cross the boundary. Arguments cannot select authority. Arm functions must be idempotent: after a crash between external success and acknowledgement, restart recovery replays the persisted declaration when the node reattaches. Persist stable correlation in the arguments rather than returning an opaque handle. A failed arm returns the structured PARK_ARM_FAILED error and abandons the tracked wait so a later signal cannot revive it.

Existing n:yield behavior is unchanged.

Agent checkpoints

Checkpoint options resolve as trait defaults, explicit agent_options.checkpoint, then node config.checkpoint overrides. Maps merge recursively; lists replace, including an explicit empty list. enabled = false disables checkpointing. No checkpoint configuration means no implicit checkpointing; Dataflow retains its existing threshold rules (zero and negative thresholds disable scheduling).

Checkpoints run before the next model turn, after the preceding tool results have settled. Bindings run before the configured function fallback, and both receive the effective options; a strict binding failure never falls back. Without a function fallback, a non-strict binding failure is recorded as a skipped checkpoint; an explicit host strict = true still fails the workflow. The host still owns marker persistence and the summary length cap. Checkpointing is conversation compaction, not durable long-term recall or a hard spending limit.

Opt-in behavior _control proposals use the shared declarative subset: agent/model/trait/tool targets, session/public metadata, and memory.compact. Proposals are persisted with their iteration. Outcomes and delegations settle before policy application; effects and completion metadata are committed via the existing node transaction machinery. Recovery applies stored proposals without rerunning resolved tools. Replay is at-least-once, so external provider writes and lifecycle hooks must be idempotent using stable host refs. Proposals apply in order against the updated selection. Explicit model, trait and tool targets survive an agent switch, including empty tool lists. Recovery reloads the selected agent before another checkpoint, lifecycle hook or model step; a failed reload fails the node instead of running the previous agent. CI also kills and restarts a runtime with a pending behavior proposal, then checks the replacement agent's first step, its settled tool result and compaction.

memory.compact = true persists a request that bypasses the token threshold, not an explicit disable or a missing provider. False is not cancellation. Requested compaction also runs when the final response ends the node, rather than depending on another model step. If a proposal also changes agent, model or overlays, compaction resolves the committed target's identity, provider and options, consistently with recovery. The provider still owns long-term memory storage, retention, retrieval and authorization. No behavior attachment means no new automatic policy/evaluator calls or loop thresholds.

Optional diagnostic retention

The host can enable native cleanup through its wippy/dataflow dependency:

parameters:
  - name: userspace.dataflow:retention_enabled
    value: true
  - name: userspace.dataflow:diagnostic_retention_days
    value: "30"
  - name: userspace.dataflow:retention_interval_seconds
    value: "300"
  - name: userspace.dataflow:retention_batch_size
    value: "1000"

retention_enabled defaults to false: the optional process service does not start until the host enables it. The other defaults are 30 days, 3600 seconds, and 500 rows per table per batch. Setting days to 0 also disables deletion. Days accept integers 0–36500, intervals 60–86400 seconds, and batches 1–1000. The service uses the host's existing target_db and process_host bindings.

Cleanup starts 30 seconds after service startup and then runs on the configured interval. Each transaction removes at most one batch from each diagnostic table. It retries on the next interval after a database or configuration error; logs from dataflow.retention report the policy, candidate counts, and deleted counts.

Only entire parent/child families whose flows are all terminal and older than the retention window qualify. Active, waiting, paused, recent, unknown-state, and unrooted families are preserved. PostgreSQL serializes eligibility with flow status changes and child insertion for each bounded transaction, using a 2-second lock timeout and a 30-second statement timeout; SQLite reserves its writer before reading eligibility. A busy database fails the batch safely and retries later.

The sweeper prunes applied commit history (except each flow's last commit) and intermediate cycle.state, cycle.function_result, node.input, node.yield, node.yield.result, and parallel.progress records. It preserves pending commits, flow and node summaries, flow inputs/outputs, node results/outputs, observations, actions, and evidence. Full diagnostic replay of pruned old flows is no longer available. This is diagnostic retention, not deletion of durable application records or all workflow history. PostgreSQL autovacuum makes deleted space reusable; the service does not run disruptive VACUUM FULL operations.

For an administrative preview, call the library userspace.dataflow.retention:sweeper.run(db, {days=30, batch_size=1000, dry_run=true}) with an authorized database handle. The library is not a public HTTP endpoint.

Live execution authority

Hosts with mutable permissions bind their native identity resolver through execution_authority_imports and set refresh_perms: true (default false). Dataflow resolves current authority at activation and resume boundaries and preserves explicit narrowing ceilings. The host binding contract describes capture, resolution and existing frame behavior.

Iteration input routes

Parallel iterator and Cycle template configurations accept optional iteration_input_routes in raw node commands:

iteration_input_routes = {
    { node_id = root_template_id, discriminator = "default" },
    { node_id = join_template_id, discriminator = "item", transform = "output" },
}

Each node_id names a direct template child of the iterator. Routes seed the Parallel item or the Cycle context (input, state, last_result, iteration) once per iteration, including nodes with incoming body edges. Discriminators participate in the target's normal inputs.required gate. Transforms use the same expression environment and error handling as data targets, with output set to the item or context.

When routes are present, they replace implicit root item seeding. When absent, the existing root seeding remains unchanged. Parallel passthrough inputs still reach roots. Graph construction rejects targets outside the body, duplicate targets, body dependency cycles, and body edges returning to the iterator. Routes do not add executable nodes or outgoing body edges. Cycle retains its existing iteration limit and fails if continuation exceeds it.

About

Agentic workflow framework: DAG, agentic nodes, signals, parallel, dynamic branching

Resources

Code of conduct

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages