Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -495,14 +495,10 @@ fn format_connected_mcp_block(
// (a malicious description could otherwise smuggle routing-overriding
// instructions into the prompt). Flatten newlines/tabs so a single
// list item can't be broken or hijacked across lines.
// Hand-added servers have no registry description. Their initialize
// instructions are the only capability hint available in that case,
// and are equally untrusted remote text.
let desc_raw = s
.description
.as_deref()
.filter(|description| !description.trim().is_empty())
.or(s.instructions.as_deref())
.unwrap_or("")
.trim();
let desc = if desc_raw.is_empty() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ fn connected_mcp_block_falls_back_to_tool_count_and_qualified_name() {
}

#[test]
fn connected_mcp_block_uses_sanitized_initialize_instructions_without_description() {
fn connected_mcp_block_falls_back_to_tool_count_without_description() {
use crate::mcp::registry::connections::ConnectedServerOverview;
let block = format_connected_mcp_block(
&[ConnectedServerOverview {
Expand Down
4 changes: 2 additions & 2 deletions crates/openhuman-core/src/flows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ from always-compiled code.
- `pub mod discovery_tools` — `SuggestWorkflowsTool`.
- `pub mod memory_tools` — `FlowMemoryRecallTool`, `FlowMemoryRememberTool`, plus `flow_namespace` / `FLOW_MEMORY_NAMESPACE_PREFIX` / `cross_flow_recall` (re-exported from `mod.rs` because the tinyflows `memory` node's `OpenHumanMemory` adapter needs byte-identical `scope: "flows"` results).
- `pub mod agents` — first-class built-in sub-agents: `workflow_builder` (authoring copilot) and `flow_discovery` (read-only suggestion scout); their `agent.toml` and `prompt::build` are referenced by path from the `BUILTINS` slice in `agent/registry/agents/loader.rs`.
- `pub mod skills` (needs both `flows` and `skills` features) — bundles `skills/flow-authoring/WORKFLOW.md`, a skill teaching flows authoring.
- `pub mod skills` (needs both `flows` and `skills` features) — registers the portable `tinyflows-copilot` `flow-authoring` manual with OpenHuman's native skill runtime.
- `pub mod tinyflows` — the capability seam (`caps/`) implementing `tinyflows`'s traits over real OpenHuman services, plus `observability.rs` (`FlowRunObserver`), `memory_adapter.rs` (`OpenHumanMemory`), and `langfuse_export.rs`. Has its own [README](tinyflows/README.md).
- Re-exported model types (from `tinyflows_catalog`, not owned here): `Flow`, `FlowConnection`, `FlowDraft`, `FlowImport`, `FlowRevision`, `FlowRun`, `FlowRunStep`, `FlowRunTrigger`, `FlowSuggestion`, `FlowValidation`, `FlowValidationError`, `SuggestionStatus`, `DraftOrigin`, plus `types`, `run_registry`, `build_registry`, and `n8n_import` (the format importer).

Expand All @@ -57,7 +57,7 @@ from always-compiled code.
- `crates/openhuman-core/src/agent/tinyagents/` — message/tool-call/usage conversions used by the `llm` and `prompt` capabilities, and `thread_context::with_thread_id` around a run; `agent` nodes run a nested harness turn through the `agent` capability (`tinyflows/caps/agent.rs`).
- `crates/openhuman-core/src/cron/` — `add_flow_schedule_job` arms a schedule-triggered flow as a `JobType::Flow` cron job; the scheduler fires it by publishing `DomainEvent::FlowScheduleTick`, which `bus::FlowTriggerSubscriber` picks up.
- `crates/openhuman-core/src/platform/socket/medulla/workflows.rs` — `WorkflowBridge` trait implemented by `medulla_bridge`.
- `crates/openhuman-core/src/skills/` — the `Workflow` / `WorkflowScope` catalogue types used by `catalogue.rs`, and the `BundledSkill` mechanism used by `skills/flow-authoring/`.
- `crates/openhuman-core/src/skills/` — the `Workflow` / `WorkflowScope` catalogue types used by `catalogue.rs`, and the native `BundledSkill` mechanism that exposes the portable `tinyflows-copilot` authoring manual.
- `crates/openhuman-core/src/memory/` — `memory_tools`/`tinyflows::memory_adapter` read/write agent memory under the `flows` scope.

## Called by
Expand Down
681 changes: 0 additions & 681 deletions crates/openhuman-core/src/flows/agents/workflow_builder/prompt.md

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -111,27 +111,7 @@ pub(super) fn tool_call_arg_null_entries(
/// node simply has no predecessors, or none of them is a condition) — the
/// warning is still emitted, just without a named culprit node.
pub(super) fn find_upstream_condition(graph: &WorkflowGraph, node_id: &str) -> Option<String> {
let mut visited: std::collections::HashSet<&str> = std::collections::HashSet::new();
let mut queue: std::collections::VecDeque<&str> = graph
.edges
.iter()
.filter(|edge| edge.to_node == node_id)
.map(|edge| edge.from_node.as_str())
.collect();
while let Some(current) = queue.pop_front() {
if !visited.insert(current) {
continue;
}
if let Some(node) = graph.nodes.iter().find(|n| n.id == current) {
if node.kind == tinyflows::model::NodeKind::Condition {
return Some(node.id.clone());
}
}
for edge in graph.edges.iter().filter(|edge| edge.to_node == current) {
queue.push_back(edge.from_node.as_str());
}
}
None
tinyflows::diagnostics::nearest_upstream_condition(graph, node_id)
}

/// Best-effort extraction of the human-readable error message the engine
Expand All @@ -143,19 +123,7 @@ pub(super) fn find_upstream_condition(graph: &WorkflowGraph, node_id: &str) -> O
/// itself (whose `diagnostics` stays empty for an error step — see
/// [`DryRunWorkflowTool::execute`]'s `node_errors` collection).
pub(super) fn tool_call_error_message(output: &Value, node_id: &str) -> Option<String> {
output
.get("nodes")?
.get(node_id)?
.get("items")?
.as_array()?
.iter()
.find_map(|item| {
item.get("json")?
.get("error")?
.get("message")?
.as_str()
.map(str::to_string)
})
tinyflows::diagnostics::node_error_message(output, node_id)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority medium critique confident

Continue past error entries without a readable message

node_error_message selects the first item containing an error field, then applies error_message to that value. If the first error is an empty object, error_message returns its JSON representation (for example, {}), so a later item containing {"message":"useful detail"} is never examined. The previous implementation skipped entries without a string error.message and would return that later readable message. Preserve the old per-item search semantics, or update the shared helper to continue searching when an error value is not a useful message.

[RULE] error-message-selection ·

}

/// The engine's own step-capturing observer, re-exported under the name
Expand Down
42 changes: 6 additions & 36 deletions crates/openhuman-core/src/flows/ops/run_rows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,21 +216,7 @@ pub(super) fn finish_flow_run_row(
/// observer didn't emit an `on_step_finish` for (notably the trigger node),
/// and as the whole-run source when the observer saw nothing at all.
fn reconstruct_steps(output: &Value) -> Vec<FlowRunStep> {
let Some(nodes) = output.get("nodes").and_then(Value::as_object) else {
return Vec::new();
};
nodes
.iter()
.map(|(node_id, slot)| FlowRunStep {
node_id: node_id.clone(),
output: slot.get("items").cloned().unwrap_or(Value::Null),
port: slot.get("port").and_then(Value::as_str).map(str::to_string),
// Reconstructed post-hoc: no live status/timing (see FlowRunStep).
status: None,
duration_ms: None,
diagnostics: Vec::new(),
})
.collect()
tinyflows_catalog::run_summary::reconstruct_steps(output)
}

/// Reads back whatever steps the live [`FlowRunObserver`] has already persisted
Expand All @@ -255,7 +241,6 @@ pub(super) fn current_persisted_steps(config: &Config, run_id: &str) -> Vec<Flow
/// (e.g. a run that paused immediately at a gate before any node finished),
/// falls back wholesale to the reconstruction.
pub(super) fn settle_steps(config: &Config, run_id: &str, output: &Value) -> Vec<FlowRunStep> {
let reconstructed = reconstruct_steps(output);
let persisted = current_persisted_steps(config, run_id);
if persisted.is_empty() {
tracing::debug!(
Expand All @@ -264,21 +249,14 @@ pub(super) fn settle_steps(config: &Config, run_id: &str, output: &Value) -> Vec
reconstructed = reconstructed.len(),
"[flows] settle_steps: no live-observed steps — using post-hoc reconstruction"
);
return reconstructed;
}
let mut merged = persisted;
let mut filled = 0usize;
for step in reconstructed {
if !merged.iter().any(|s| s.node_id == step.node_id) {
merged.push(step);
filled += 1;
}
return reconstruct_steps(output);
}
let merged = tinyflows_catalog::run_summary::settle_steps(persisted, output);
tracing::debug!(
target: "flows",
run_id,
step_count = merged.len(),
filled_from_reconstruction = filled,
filled_from_reconstruction = merged.len(),
"[flows] settle_steps: merged live-observed steps with post-hoc reconstruction"
);
merged
Expand Down Expand Up @@ -336,14 +314,6 @@ pub(super) fn finalize_terminal_status(
settled: &[FlowRunStep],
pending_approvals: &[String],
) -> (&'static str, Option<String>) {
if !pending_approvals.is_empty() {
return ("pending_approval", None);
}
let status = degrade_completed_status(settled);
let error = if status == "failed" {
failed_step_error_summary(settled)
} else {
None
};
(status, error)
let summary = tinyflows_catalog::run_summary::terminal_status(settled, pending_approvals);
(summary.status, summary.error)
}
53 changes: 0 additions & 53 deletions crates/openhuman-core/src/flows/skills/flow-authoring/WORKFLOW.md

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

19 changes: 6 additions & 13 deletions crates/openhuman-core/src/flows/skills/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,8 @@
//! pins it in the prompt — correctly. It constrains an instinct the model has
//! before it would think to consult anything.
//!
//! # Where this belongs eventually
//!
//! Upstream, in tinyflows. The pages name no OpenHuman type and no host
//! concept beyond the tool slugs, so moving them is a directory move plus a
//! changed `include_str!` path. The pinned `vendor/tinyflows` submodule has no
//! crate to hold them yet — there is no `tinyflows-copilot` in it — so they sit
//! with the flows domain here in the meantime. Keeping them free of host
//! coupling is what keeps that move cheap; do not reach into `crate::` from a
//! page.
//! The manual's bytes live in `tinyflows-copilot`; this host only converts the
//! portable file list into its native bundled-skill registration.

use crate::skills::bundled::{BundledFile, BundledSkill};

Expand All @@ -45,19 +38,19 @@ pub const FLOW_AUTHORING: BundledSkill = BundledSkill {
files: &[
BundledFile {
path: "WORKFLOW.md",
contents: include_str!("flow-authoring/WORKFLOW.md"),
contents: tinyflows_copilot::resources::FLOW_AUTHORING_WORKFLOW,
},
BundledFile {
path: "references/expressions.md",
contents: include_str!("flow-authoring/references/expressions.md"),
contents: tinyflows_copilot::resources::FLOW_AUTHORING_EXPRESSIONS,
},
BundledFile {
path: "references/node-config.md",
contents: include_str!("flow-authoring/references/node-config.md"),
contents: tinyflows_copilot::resources::FLOW_AUTHORING_NODE_CONFIG,
},
BundledFile {
path: "references/dry-run.md",
contents: include_str!("flow-authoring/references/dry-run.md"),
contents: tinyflows_copilot::resources::FLOW_AUTHORING_DRY_RUN,
},
],
};
Expand Down
Loading
Loading