Skip to content

Commit 5abb92f

Browse files
senamakelmedullabot
andcommitted
docs(rules): document indirect call arguments
The rules README and tool-rules spec now spell out that an indirect target is carried as an IndirectCall with its own arguments, and that evaluation uses those arguments so an argument-scoped rule cannot be sidestepped through a dispatcher envelope. The re-exports and test formatting were tidied to match. Auto-committed-on: dragonfly Co-authored-by: Medulla <medulla@tinyhumans.ai>
1 parent 42b892a commit 5abb92f

4 files changed

Lines changed: 20 additions & 12 deletions

File tree

‎crates/tinytools/src/lib.rs‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,9 @@ pub use rank::{
150150
};
151151
pub use result::{FileData, ImageData, ToolContent, ToolControl, ToolErrorKind, ToolResult};
152152
pub use rules::{
153-
ApprovalDirective, ArgMatcher, DefaultEffect, Patterns, RuleContext, RuleDecision, RuleEffect,
154-
IndirectCall, RuleRef, SideEffect, Surface, ToolMatcher, ToolRule, ToolRuleSet, ToolRules,
155-
ToolSubject,
156-
glob_matches,
153+
ApprovalDirective, ArgMatcher, DefaultEffect, IndirectCall, Patterns, RuleContext,
154+
RuleDecision, RuleEffect, RuleRef, SideEffect, Surface, ToolMatcher, ToolRule, ToolRuleSet,
155+
ToolRules, ToolSubject, glob_matches,
157156
};
158157
pub use shared::{SharedTool, owned_belt, share_belt};
159158
pub use spec::ToolSpec;

‎crates/tinytools/src/rules/README.md‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,10 @@ same line `deferral` draws.
3434
- **`ToolRuleSet`** stacks layers, and every layer must admit a tool. Adding a
3535
layer can only narrow, so two allowlists intersect.
3636
- **`ToolRuleSet::evaluate_call`** evaluates a call with the tool's
37-
argument-aware permission. When the tool reports an `indirect_target`, it
38-
evaluates that target too.
37+
argument-aware permission. When the tool reports an `indirect_target`, an
38+
`IndirectCall { target, arguments }`, it evaluates that target too. It uses
39+
the target's own arguments when the dispatcher wraps them in an envelope, so
40+
an argument-scoped rule cannot be sidestepped through the dispatcher.
3941

4042
## Operational constraints
4143

‎crates/tinytools/src/rules/mod_tests.rs‎

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -415,9 +415,8 @@ impl Tool for Execute {
415415
fn indirect_target(&self, args: &Value) -> Option<IndirectCall> {
416416
let action = args.get("action")?.as_str()?;
417417
let toolkit = action.split('_').next()?;
418-
let call = IndirectCall::new(
419-
ToolSubject::named(action).with_family(toolkit.to_ascii_lowercase()),
420-
);
418+
let call =
419+
IndirectCall::new(ToolSubject::named(action).with_family(toolkit.to_ascii_lowercase()));
421420
Some(match args.get("arguments") {
422421
Some(inner) => call.with_arguments(inner.clone()),
423422
None => call,
@@ -472,8 +471,14 @@ fn evaluate_call_reads_the_targets_own_arguments() {
472471
)
473472
};
474473
let refused = wrapped(true);
475-
assert!(!refused.callable, "the envelope does not hide the target's arguments");
476-
assert_eq!(refused.blocked_by.expect("blocked").id.as_deref(), Some("no-permanent-delete"));
474+
assert!(
475+
!refused.callable,
476+
"the envelope does not hide the target's arguments"
477+
);
478+
assert_eq!(
479+
refused.blocked_by.expect("blocked").id.as_deref(),
480+
Some("no-permanent-delete")
481+
);
477482
assert!(wrapped(false).callable);
478483
let call = IndirectCall::from(ToolSubject::named("x"));
479484
assert_eq!(call.arguments, None);

‎docs/specs/tool-rules.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ search, and a wildcard worked in one list but not the next.
5656
hand. `RuleDecision { visible, callable, approval, blocked_by }` names the
5757
layer and rule behind a refusal; `refusal(tool)` renders it for the model.
5858
- `ToolRuleSet::evaluate_call(tool, ctx, args)` evaluates the tool with its
59-
argument-aware permission and, when `Tool::indirect_target` names a target,
59+
argument-aware permission and, when `Tool::indirect_target` returns an
60+
`IndirectCall { target, arguments }`, evaluates the target against its own
61+
arguments (falling back to the dispatcher's) —
6062
the target too.
6163
- `Tool` gains two defaulted, descriptive methods: `tags()` and
6264
`indirect_target(args)`.

0 commit comments

Comments
 (0)