flowproof 0.6.1__tar.gz → 0.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {flowproof-0.6.1 → flowproof-0.7.0}/Cargo.lock +7 -7
- {flowproof-0.6.1 → flowproof-0.7.0}/Cargo.toml +1 -1
- {flowproof-0.6.1 → flowproof-0.7.0}/PKG-INFO +1 -1
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-adapters/src/agent_runner.rs +114 -1
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/src/agent_flow.rs +2 -1
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/tests/examples_resolve.rs +56 -19
- {flowproof-0.6.1 → flowproof-0.7.0}/flowproof/__init__.py +1 -1
- {flowproof-0.6.1 → flowproof-0.7.0}/pyproject.toml +1 -1
- {flowproof-0.6.1 → flowproof-0.7.0}/README.md +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-adapters/Cargo.toml +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-adapters/src/agent_proxy.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-adapters/src/egress.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-adapters/src/egress_linux.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-adapters/src/lib.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-adapters/src/mcp_core.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-adapters/src/mcp_http.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-adapters/src/mcp_stdio.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-adapters/src/sap_com.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-adapters/src/vision.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-adapters/src/web.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-agent/Cargo.toml +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-agent/src/agent_steps.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-agent/src/author.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-agent/src/clarify.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-agent/src/heal.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-agent/src/lib.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-agent/src/llm.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-agent/src/recorder.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-agent/src/rules.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-agent/src/spec.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/Cargo.toml +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/src/capture.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/src/lib.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/src/main.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/tests/agent_flow_e2e.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/tests/api_pipeline.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/tests/audit_record_e2e.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/tests/calc_e2e.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/tests/clock_e2e.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/tests/cookie_e2e.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/tests/egress_e2e.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/tests/grid_cell_e2e.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/tests/iframe_e2e.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/tests/llm_author_e2e.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/tests/mcp_stdio_e2e.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/tests/notepad_author_e2e.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/tests/notepad_e2e.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/tests/page_title_e2e.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/tests/sap_e2e.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/tests/sap_pipeline.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/tests/sap_sim_e2e.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/tests/scoped_container_e2e.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/tests/skip_unless_env.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/tests/suite_env_from.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/tests/suite_flow_isolation.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/tests/suite_missing_trace.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/tests/support/sap_simulator.py +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/tests/vision_pipeline.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-cli/tests/web_e2e.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-driver/Cargo.toml +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-driver/src/app.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-driver/src/backend.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-driver/src/gdi.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-driver/src/lib.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-driver/src/mock.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-driver/src/oob.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-driver/src/recording.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-driver/src/redact.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-driver/src/visual.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-driver/src/window.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-python/Cargo.toml +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-python/src/lib.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-replay/Cargo.toml +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-replay/src/lib.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-replay/src/report.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-replay/src/runrecord.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-replay/tests/replay_calc.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-trace/Cargo.toml +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-trace/schema/trace-v1.schema.json +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-trace/src/cassette.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-trace/src/cassette_diff.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-trace/src/egress.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-trace/src/format.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-trace/src/lib.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-trace/src/secret.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-trace/src/secret_scan.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-trace/src/substitution.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-trace/src/toolcalls.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-trace/tests/fixtures/sample.trace.jsonl +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-trace/tests/schema_conformance.rs +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/flowproof/cli.py +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/flowproof/flow.py +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/flowproof/mcp_server.py +0 -0
- {flowproof-0.6.1 → flowproof-0.7.0}/flowproof/py.typed +0 -0
|
@@ -760,7 +760,7 @@ dependencies = [
|
|
|
760
760
|
|
|
761
761
|
[[package]]
|
|
762
762
|
name = "flowproof-adapters"
|
|
763
|
-
version = "0.
|
|
763
|
+
version = "0.7.0"
|
|
764
764
|
dependencies = [
|
|
765
765
|
"anyhow",
|
|
766
766
|
"flowproof-driver",
|
|
@@ -779,7 +779,7 @@ dependencies = [
|
|
|
779
779
|
|
|
780
780
|
[[package]]
|
|
781
781
|
name = "flowproof-agent"
|
|
782
|
-
version = "0.
|
|
782
|
+
version = "0.7.0"
|
|
783
783
|
dependencies = [
|
|
784
784
|
"chrono",
|
|
785
785
|
"flowproof-driver",
|
|
@@ -795,7 +795,7 @@ dependencies = [
|
|
|
795
795
|
|
|
796
796
|
[[package]]
|
|
797
797
|
name = "flowproof-cli"
|
|
798
|
-
version = "0.
|
|
798
|
+
version = "0.7.0"
|
|
799
799
|
dependencies = [
|
|
800
800
|
"ab_glyph",
|
|
801
801
|
"chrono",
|
|
@@ -816,7 +816,7 @@ dependencies = [
|
|
|
816
816
|
|
|
817
817
|
[[package]]
|
|
818
818
|
name = "flowproof-driver"
|
|
819
|
-
version = "0.
|
|
819
|
+
version = "0.7.0"
|
|
820
820
|
dependencies = [
|
|
821
821
|
"image",
|
|
822
822
|
"postgres",
|
|
@@ -830,7 +830,7 @@ dependencies = [
|
|
|
830
830
|
|
|
831
831
|
[[package]]
|
|
832
832
|
name = "flowproof-python"
|
|
833
|
-
version = "0.
|
|
833
|
+
version = "0.7.0"
|
|
834
834
|
dependencies = [
|
|
835
835
|
"flowproof-agent",
|
|
836
836
|
"flowproof-cli",
|
|
@@ -842,7 +842,7 @@ dependencies = [
|
|
|
842
842
|
|
|
843
843
|
[[package]]
|
|
844
844
|
name = "flowproof-replay"
|
|
845
|
-
version = "0.
|
|
845
|
+
version = "0.7.0"
|
|
846
846
|
dependencies = [
|
|
847
847
|
"chrono",
|
|
848
848
|
"flowproof-agent",
|
|
@@ -856,7 +856,7 @@ dependencies = [
|
|
|
856
856
|
|
|
857
857
|
[[package]]
|
|
858
858
|
name = "flowproof-trace"
|
|
859
|
-
version = "0.
|
|
859
|
+
version = "0.7.0"
|
|
860
860
|
dependencies = [
|
|
861
861
|
"jsonschema",
|
|
862
862
|
"regex",
|
|
@@ -3,7 +3,7 @@ resolver = "2"
|
|
|
3
3
|
members = ["crates/flowproof-driver", "crates/flowproof-trace", "crates/flowproof-replay", "crates/flowproof-agent", "crates/flowproof-adapters", "crates/flowproof-cli", "crates/flowproof-python"]
|
|
4
4
|
|
|
5
5
|
[workspace.package]
|
|
6
|
-
version = "0.
|
|
6
|
+
version = "0.7.0"
|
|
7
7
|
edition = "2021"
|
|
8
8
|
license = "Apache-2.0"
|
|
9
9
|
repository = "https://github.com/automators-com/flowproof"
|
|
@@ -277,12 +277,54 @@ fn configure(
|
|
|
277
277
|
child.env("FLOWPROOF_LLM_PROXY", base);
|
|
278
278
|
// The spec's own env goes on LAST so a flow can override any of the
|
|
279
279
|
// above; it knows its client better than this module does.
|
|
280
|
+
//
|
|
281
|
+
// Values may reference runtime handles the spec cannot know when it is
|
|
282
|
+
// written, because the proxy binds an ephemeral port. Without this, an
|
|
283
|
+
// agent whose client reads a non-standard variable (an AI gateway URL,
|
|
284
|
+
// say) cannot be pointed at the proxy from the spec at all, and every
|
|
285
|
+
// such adopter writes the same wrapper script.
|
|
280
286
|
for (key, value) in env {
|
|
281
|
-
child.env(key, value);
|
|
287
|
+
child.env(key, substitute_runtime_handles(value, base, env));
|
|
282
288
|
}
|
|
283
289
|
Ok(child)
|
|
284
290
|
}
|
|
285
291
|
|
|
292
|
+
/// Replace the `${flowproof.*}` handles with values only known at spawn time.
|
|
293
|
+
///
|
|
294
|
+
/// - `${flowproof.proxy_url}` - the model proxy, `/v1` included, the form
|
|
295
|
+
/// OpenAI-compatible clients expect.
|
|
296
|
+
/// - `${flowproof.proxy_url_no_v1}` - the same origin without `/v1`, for a
|
|
297
|
+
/// client that appends its own path (the Anthropic SDK does).
|
|
298
|
+
/// - `${flowproof.mcp_url.<name>}` - the stand-in URL for that MCP server,
|
|
299
|
+
/// for a client that builds its endpoints from a base rather than taking
|
|
300
|
+
/// one URL per server.
|
|
301
|
+
///
|
|
302
|
+
/// Anything else is left untouched: an unknown handle is far more likely to
|
|
303
|
+
/// be a value that happens to look like one than a typo worth failing on,
|
|
304
|
+
/// and `${VAR}` secret refs have already been resolved by this point.
|
|
305
|
+
fn substitute_runtime_handles(value: &str, base: &str, env: &BTreeMap<String, String>) -> String {
|
|
306
|
+
if !value.contains("${flowproof.") {
|
|
307
|
+
return value.to_string();
|
|
308
|
+
}
|
|
309
|
+
let mut out = value
|
|
310
|
+
.replace("${flowproof.proxy_url_no_v1}", &anthropic_base(base))
|
|
311
|
+
.replace("${flowproof.proxy_url}", base);
|
|
312
|
+
// The MCP stand-in URLs are already in this same map, injected as
|
|
313
|
+
// `FLOWPROOF_MCP_URL_<NAME>`; this is a friendlier spelling of the same
|
|
314
|
+
// value, so the two can never disagree.
|
|
315
|
+
for (key, url) in env {
|
|
316
|
+
if let Some(name) = key.strip_prefix("FLOWPROOF_MCP_URL_") {
|
|
317
|
+
out = out.replace(&format!("${{flowproof.mcp_url.{name}}}"), url);
|
|
318
|
+
// Server names are lowercase in the spec; the env var is upper.
|
|
319
|
+
out = out.replace(
|
|
320
|
+
&format!("${{flowproof.mcp_url.{}}}", name.to_ascii_lowercase()),
|
|
321
|
+
url,
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
out
|
|
326
|
+
}
|
|
327
|
+
|
|
286
328
|
/// Wait for `child` to the deadline, killing it at the timeout. Returns the
|
|
287
329
|
/// exit status (`None` if killed or unwaitable) and whether it timed out.
|
|
288
330
|
fn wait_to_deadline(
|
|
@@ -434,6 +476,77 @@ pub fn run_against_contained(
|
|
|
434
476
|
|
|
435
477
|
#[cfg(test)]
|
|
436
478
|
mod tests {
|
|
479
|
+
|
|
480
|
+
/// The gap a real adopter hit: their client reads AI_GATEWAY_URL, and
|
|
481
|
+
/// the proxy's port is not known when the spec is written, so a static
|
|
482
|
+
/// `agent.env` value could not reach it.
|
|
483
|
+
#[test]
|
|
484
|
+
fn the_proxy_url_is_addressable_from_a_spec_env_value() {
|
|
485
|
+
let env = BTreeMap::new();
|
|
486
|
+
let got =
|
|
487
|
+
substitute_runtime_handles("${flowproof.proxy_url}", "http://127.0.0.1:51234/v1", &env);
|
|
488
|
+
assert_eq!(got, "http://127.0.0.1:51234/v1");
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/// Some clients append their own `/v1`, so handing them the suffixed
|
|
492
|
+
/// form produces `/v1/v1`. The no-suffix handle is for those.
|
|
493
|
+
#[test]
|
|
494
|
+
fn the_no_v1_handle_strips_the_suffix() {
|
|
495
|
+
let env = BTreeMap::new();
|
|
496
|
+
let got = substitute_runtime_handles(
|
|
497
|
+
"${flowproof.proxy_url_no_v1}",
|
|
498
|
+
"http://127.0.0.1:51234/v1",
|
|
499
|
+
&env,
|
|
500
|
+
);
|
|
501
|
+
assert_eq!(got, "http://127.0.0.1:51234");
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/// A client that builds several endpoints off one base (`<base>/mcp`,
|
|
505
|
+
/// `<base>/mcp-exec/sap`) has no per-server variable to override, so it
|
|
506
|
+
/// needs the stand-in's base by name.
|
|
507
|
+
#[test]
|
|
508
|
+
fn an_mcp_stand_in_is_addressable_by_server_name() {
|
|
509
|
+
let mut env = BTreeMap::new();
|
|
510
|
+
env.insert(
|
|
511
|
+
"FLOWPROOF_MCP_URL_DATAMAKER_EXEC".to_string(),
|
|
512
|
+
"http://127.0.0.1:44100/mcp".to_string(),
|
|
513
|
+
);
|
|
514
|
+
// Upper (as the var is spelled) and lower (as the spec names it).
|
|
515
|
+
for handle in [
|
|
516
|
+
"${flowproof.mcp_url.DATAMAKER_EXEC}",
|
|
517
|
+
"${flowproof.mcp_url.datamaker_exec}",
|
|
518
|
+
] {
|
|
519
|
+
let got = substitute_runtime_handles(handle, "http://127.0.0.1:1/v1", &env);
|
|
520
|
+
assert_eq!(got, "http://127.0.0.1:44100/mcp", "{handle}");
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/// Handles interpolate INTO a larger value, because that is how a base
|
|
525
|
+
/// URL is actually used.
|
|
526
|
+
#[test]
|
|
527
|
+
fn a_handle_substitutes_inside_a_longer_value() {
|
|
528
|
+
let env = BTreeMap::new();
|
|
529
|
+
let got = substitute_runtime_handles(
|
|
530
|
+
"${flowproof.proxy_url_no_v1}/custom/path",
|
|
531
|
+
"http://127.0.0.1:9/v1",
|
|
532
|
+
&env,
|
|
533
|
+
);
|
|
534
|
+
assert_eq!(got, "http://127.0.0.1:9/custom/path");
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/// A value that is not a handle must survive untouched - including one
|
|
538
|
+
/// that merely looks shell-ish. Failing on an unknown handle would be
|
|
539
|
+
/// worse than passing it through.
|
|
540
|
+
#[test]
|
|
541
|
+
fn ordinary_values_and_unknown_handles_pass_through() {
|
|
542
|
+
let env = BTreeMap::new();
|
|
543
|
+
for value in ["plain", "$HOME/x", "${flowproof.not_a_handle}"] {
|
|
544
|
+
assert_eq!(
|
|
545
|
+
substitute_runtime_handles(value, "http://127.0.0.1:1/v1", &env),
|
|
546
|
+
value
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
437
550
|
use super::*;
|
|
438
551
|
use flowproof_trace::cassette::{Message, ToolCall, Turn, TurnRequest, TurnResponse};
|
|
439
552
|
|
|
@@ -1007,7 +1007,8 @@ fn unprotected_tool_warning(spec: &FlowSpec, plan: &Plan, phase: &str) -> Option
|
|
|
1007
1007
|
intercept tool execution - {consequence}.\n \
|
|
1008
1008
|
For a tool with real side effects, declare it under `mcp:` with a `result:` \
|
|
1009
1009
|
(flowproof answers it and the real server never runs it), or stub/sandbox it \
|
|
1010
|
-
author-side. See
|
|
1010
|
+
author-side. See \
|
|
1011
|
+
https://github.com/automators-com/flowproof/blob/main/docs/agent-testing.md."
|
|
1011
1012
|
))
|
|
1012
1013
|
}
|
|
1013
1014
|
|
|
@@ -12,6 +12,7 @@ const CONN_TEST_SPEC: &str = include_str!("../../../examples/api/connection-test
|
|
|
12
12
|
/// from `npx flowproof` runs, so it has to keep parsing.
|
|
13
13
|
const AGENT_NODE_SPEC: &str = include_str!("../../../examples/agent-demo/weather-node.flow.yaml");
|
|
14
14
|
const AGENT_PY_SPEC: &str = include_str!("../../../examples/agent-demo/weather.flow.yaml");
|
|
15
|
+
const DEMO_SPEC: &str = include_str!("../../../scripts/demo/order-status.flow.yaml");
|
|
15
16
|
|
|
16
17
|
#[test]
|
|
17
18
|
fn connection_test_example_resolves_with_body_and_headers() {
|
|
@@ -119,23 +120,17 @@ fn both_agent_demos_resolve_and_assert_the_same_thing() {
|
|
|
119
120
|
);
|
|
120
121
|
}
|
|
121
122
|
|
|
122
|
-
/// The
|
|
123
|
-
/// door for the npm audience, so
|
|
124
|
-
///
|
|
125
|
-
/// the worst possible first experience.
|
|
123
|
+
/// The two quickstarts are the most-read prose we have and they are now the
|
|
124
|
+
/// front door for the npm audience, so their YAML must not drift from the file
|
|
125
|
+
/// they claim to quote. A reader who copies a block that no longer parses is
|
|
126
|
+
/// the worst possible first experience. Both the README and
|
|
127
|
+
/// docs/getting-started.md open on the same shipped example, so both are held
|
|
128
|
+
/// to it here.
|
|
126
129
|
#[test]
|
|
127
130
|
fn the_quickstart_quotes_the_shipped_agent_example_verbatim() {
|
|
131
|
+
const README: &str = include_str!("../../../README.md");
|
|
128
132
|
const DOC: &str = include_str!("../../../docs/getting-started.md");
|
|
129
133
|
|
|
130
|
-
// Pull the fenced block that names the example file.
|
|
131
|
-
let marker = "```yaml\n# examples/agent-demo/weather-node.flow.yaml\n";
|
|
132
|
-
let start = DOC
|
|
133
|
-
.find(marker)
|
|
134
|
-
.expect("quickstart quotes the node example")
|
|
135
|
-
+ marker.len();
|
|
136
|
-
let block = &DOC[start..];
|
|
137
|
-
let block = &block[..block.find("```").expect("fence closes")];
|
|
138
|
-
|
|
139
134
|
// The shipped file, minus its comment header.
|
|
140
135
|
let shipped: String = AGENT_NODE_SPEC
|
|
141
136
|
.lines()
|
|
@@ -143,12 +138,54 @@ fn the_quickstart_quotes_the_shipped_agent_example_verbatim() {
|
|
|
143
138
|
.collect::<Vec<_>>()
|
|
144
139
|
.join("\n");
|
|
145
140
|
|
|
146
|
-
|
|
147
|
-
block.
|
|
148
|
-
|
|
149
|
-
|
|
141
|
+
for (name, prose) in [("README.md", README), ("docs/getting-started.md", DOC)] {
|
|
142
|
+
// Pull the fenced block that names the example file.
|
|
143
|
+
let marker = "```yaml\n# examples/agent-demo/weather-node.flow.yaml\n";
|
|
144
|
+
let start = prose
|
|
145
|
+
.find(marker)
|
|
146
|
+
.unwrap_or_else(|| panic!("{name} quotes the node example"))
|
|
147
|
+
+ marker.len();
|
|
148
|
+
let block = &prose[start..];
|
|
149
|
+
let block = &block[..block.find("```").expect("fence closes")];
|
|
150
|
+
|
|
151
|
+
assert_eq!(
|
|
152
|
+
block.trim(),
|
|
153
|
+
shipped.trim(),
|
|
154
|
+
"{name} has drifted from examples/agent-demo/weather-node.flow.yaml"
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
// And it must still parse, so the block a reader copies actually runs.
|
|
158
|
+
FlowSpec::parse(block).expect("the quoted quickstart block parses");
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/// The flow behind the README's demo GIF. The GIF is a capture of this spec
|
|
163
|
+
/// running, so a grammar change that breaks it would silently make the README's
|
|
164
|
+
/// hero image a picture of something that no longer works.
|
|
165
|
+
#[test]
|
|
166
|
+
fn readme_demo_spec_resolves() {
|
|
167
|
+
let spec = FlowSpec::parse(DEMO_SPEC).expect("demo spec parses");
|
|
168
|
+
assert_eq!(spec.app.id(), "agent");
|
|
169
|
+
|
|
170
|
+
// Deliberately unmocked: the demo's tool returns a deterministic result, so
|
|
171
|
+
// replay needs no substitution and the run earns no unprotected-tool
|
|
172
|
+
// warning. That is what keeps the captured output to five lines.
|
|
173
|
+
let tool = spec
|
|
174
|
+
.tools
|
|
175
|
+
.iter()
|
|
176
|
+
.find(|t| t.name == "lookup_order")
|
|
177
|
+
.expect("lookup_order declared");
|
|
178
|
+
assert!(
|
|
179
|
+
tool.result.is_null(),
|
|
180
|
+
"the demo tool must stay declaration-only"
|
|
150
181
|
);
|
|
151
182
|
|
|
152
|
-
//
|
|
153
|
-
|
|
183
|
+
// Paths are relative to the repository root, because that is where the GIF
|
|
184
|
+
// runs its commands and therefore what a reader copies.
|
|
185
|
+
let command = spec
|
|
186
|
+
.agent
|
|
187
|
+
.as_ref()
|
|
188
|
+
.and_then(|a| a.command.clone())
|
|
189
|
+
.expect("demo has a command");
|
|
190
|
+
assert_eq!(command, "python3 scripts/demo/support_agent.py");
|
|
154
191
|
}
|
|
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "flowproof"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.7.0"
|
|
8
8
|
description = "Deterministic tests for AI agents: record a run once, replay it with zero LLM calls. Assert which tools an agent called, with which arguments, in which order."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "Apache-2.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flowproof-0.6.1 → flowproof-0.7.0}/crates/flowproof-trace/tests/fixtures/sample.trace.jsonl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|