Skip to content
Closed
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
2 changes: 1 addition & 1 deletion harness/src/functions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ pub fn register_all(iii: &Arc<IIIClient>, deps: &Arc<Deps>) {
deps,
react::REACT_ID,
react::REACT_DESC,
|d, ev: Value, meta| async move { react::handle(&d, ev, meta).await },
|d, ev: react::ReactEvent, meta| async move { react::handle(&d, ev.0, meta).await },
);

tracing::info!("all harness::* functions registered");
Expand Down
44 changes: 44 additions & 0 deletions harness/src/functions/react.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,50 @@ pub const REACT_DESC: &str =
events and callbacks: bind it via engine::register_trigger with the sub-agent spec in \
`metadata`.";

/// Fired-event payload of a reactive subscription: arbitrary JSON produced by
/// the originating trigger, then appended to the spawned sub-agent task.
///
/// The handler keeps a lossless `serde_json::Value` payload internally because
/// trigger events are not always objects. This wrapper exists so registration
/// publishes a real schema instead of `AnyValue`, which the registry publish
/// gate rejects.
#[derive(Debug, Clone, Deserialize)]
#[serde(transparent)]
pub struct ReactEvent(pub Value);

impl JsonSchema for ReactEvent {
fn schema_name() -> String {
"ReactEvent".to_string()
}

fn json_schema(_: &mut schemars::r#gen::SchemaGenerator) -> schemars::schema::Schema {
use schemars::schema::{InstanceType, Metadata, SchemaObject};
SchemaObject {
instance_type: Some(
vec![
InstanceType::Null,
InstanceType::Boolean,
InstanceType::Number,
InstanceType::String,
InstanceType::Array,
InstanceType::Object,
]
.into(),
),
metadata: Some(Box::new(Metadata {
description: Some(
"Arbitrary fired-event payload from the subscribed trigger; appended to the \
spawned sub-agent task."
.to_string(),
),
..Default::default()
})),
..Default::default()
}
.into()
}
}

/// iii-state scope holding join accumulator records (one key per `join.id`).
const JOIN_SCOPE: &str = "harness::react_join";

Expand Down
3 changes: 3 additions & 0 deletions harness/src/surface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
//! `harness::on-config-change`) are intentionally excluded — they are not part
//! of the agent-facing surface.

use crate::functions::react::REACT_ID;
use crate::functions::{
function_resolve::{FunctionResolveRequest, FunctionResolveResponse},
function_trigger::{FunctionTriggerRequest, FunctionTriggerResponse},
react::{ReactEvent, ReactResult},
send::{SendRequest, SendResponse},
spawn::{SpawnRequest, SpawnResponse},
status::{StatusReport, StatusRequest},
Expand Down Expand Up @@ -62,5 +64,6 @@ pub fn catalog() -> Vec<FunctionSpec> {
spec::<FunctionResolveRequest, FunctionResolveResponse>(FUNCTION_RESOLVE_ID),
spec::<StopRequest, StopResponse>(STOP_ID),
spec::<StatusRequest, Option<StatusReport>>(STATUS_ID),
spec::<ReactEvent, ReactResult>(REACT_ID),
]
}
44 changes: 44 additions & 0 deletions harness/tests/golden/schemas/harness.react.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"function_id": "harness::react",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "Arbitrary fired-event payload from the subscribed trigger; appended to the spawned sub-agent task.",
"title": "ReactEvent",
"type": [
"null",
"boolean",
"number",
"string",
"array",
"object"
]
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"child_session_id": {
"description": "The spawned sub-agent's child session id, when spawn returned one.",
"type": [
"string",
"null"
]
},
"note": {
"description": "Why nothing spawned (missing spec, join not yet complete, already fired, error). Present iff `!spawned`.",
"type": [
"string",
"null"
]
},
"spawned": {
"description": "Whether a `harness::spawn` was dispatched this call.",
"type": "boolean"
}
},
"required": [
"spawned"
],
"title": "ReactResult",
"type": "object"
}
}
1 change: 1 addition & 0 deletions harness/tests/schemas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ fn catalog_lists_all_functions_in_registration_order() {
"harness::function::resolve",
"harness::stop",
"harness::status",
"harness::react",
]
);
}
Expand Down
Loading