@@ -34,7 +34,7 @@ describe("tool execute before handler", () => {
3434 routingOutcomes . clearAll ( ) ;
3535 } ) ;
3636
37- it ( "throws on denied WebFetch calls" , async ( ) => {
37+ it ( "throws guidance on denied WebFetch calls" , async ( ) => {
3838 const canonicalizer = new MockSessionCanonicalizer ( ) ;
3939 canonicalizer . cached . set ( "root-session" , "root-session" ) ;
4040 const handler = createToolBeforeHandler ( {
@@ -55,7 +55,7 @@ describe("tool execute before handler", () => {
5555 { args : { url : "https://example.com" } } as never ,
5656 ) ,
5757 Error ,
58- "Tool denied (WebFetch) " ,
58+ "WebFetch is blocked " ,
5959 ) ;
6060
6161 assertEquals ( routingOutcomes . take ( "call-1" ) , {
@@ -65,7 +65,7 @@ describe("tool execute before handler", () => {
6565 } ) ;
6666 } ) ;
6767
68- it ( "throws on denied WebFetch calls from a child session after first-call canonical lookup" , async ( ) => {
68+ it ( "throws guidance on denied WebFetch calls from a child session after first-call canonical lookup" , async ( ) => {
6969 const canonicalizer = new MockSessionCanonicalizer ( ) ;
7070 canonicalizer . resolved . set ( "child-session" , "root-session" ) ;
7171 const handler = createToolBeforeHandler ( {
@@ -86,7 +86,7 @@ describe("tool execute before handler", () => {
8686 { args : { url : "https://example.com" } } as never ,
8787 ) ,
8888 Error ,
89- "Tool denied (WebFetch) " ,
89+ "WebFetch is blocked " ,
9090 ) ;
9191
9292 assertEquals ( canonicalizer . cachedCalls , [ "child-session" ] ) ;
@@ -98,7 +98,7 @@ describe("tool execute before handler", () => {
9898 } ) ;
9999 } ) ;
100100
101- it ( "throws a stable denial message without embedding guidance text" , async ( ) => {
101+ it ( "throws the deny guidance text when provided " , async ( ) => {
102102 const canonicalizer = new MockSessionCanonicalizer ( ) ;
103103 canonicalizer . cached . set ( "root-session" , "root-session" ) ;
104104 const handler = createToolBeforeHandler ( {
@@ -124,16 +124,54 @@ describe("tool execute before handler", () => {
124124 { args : { command : "curl https://example.com" } } as never ,
125125 ) ,
126126 Error ,
127- "Tool denied (Bash) " ,
127+ "Dynamic guidance details that should stay out of the thrown error. " ,
128128 ) ;
129129
130- assertEquals ( error . message , "Tool denied (Bash)" ) ;
130+ assertEquals (
131+ error . message ,
132+ "Dynamic guidance details that should stay out of the thrown error." ,
133+ ) ;
131134 assertStringIncludes (
132135 String ( routingOutcomes . take ( "call-stable-deny" ) ?. reason ) ,
133136 "test-deny" ,
134137 ) ;
135138 } ) ;
136139
140+ it ( "falls back to the generic denial message when guidance is absent" , async ( ) => {
141+ const canonicalizer = new MockSessionCanonicalizer ( ) ;
142+ canonicalizer . cached . set ( "root-session" , "root-session" ) ;
143+ const handler = createToolBeforeHandler ( {
144+ sessionCanonicalizer : canonicalizer as never ,
145+ guidanceThrottle : new ToolGuidanceCache ( ) ,
146+ routingOutcomes,
147+ routeToolCall : ( ) => ( {
148+ action : "deny" ,
149+ reason : "test-deny-no-guidance" ,
150+ guidance : "" ,
151+ } ) ,
152+ } ) ;
153+
154+ const error = await assertRejects (
155+ ( ) =>
156+ handler (
157+ {
158+ tool : "Bash" ,
159+ sessionID : "root-session" ,
160+ callID : "call-generic-deny" ,
161+ } as never ,
162+ { args : { command : "curl https://example.com" } } as never ,
163+ ) ,
164+ Error ,
165+ "Tool denied (Bash)" ,
166+ ) ;
167+
168+ assertEquals ( error . message , "Tool denied (Bash)" ) ;
169+ assertStringIncludes (
170+ String ( routingOutcomes . take ( "call-generic-deny" ) ?. reason ) ,
171+ "test-deny-no-guidance" ,
172+ ) ;
173+ } ) ;
174+
137175 it ( "mutates args for Bash rewrite cases" , async ( ) => {
138176 const canonicalizer = new MockSessionCanonicalizer ( ) ;
139177 canonicalizer . cached . set ( "root-session" , "root-session" ) ;
0 commit comments