@@ -3,6 +3,7 @@ import assert from "node:assert/strict";
33import {
44 createAgentSdkCodingAgentDriver ,
55 type AgentSdkQueryFn ,
6+ type CreateAgentSdkDriverOptions ,
67 type CodingAgentDriverTask ,
78} from "../dist/index.js" ;
89
@@ -35,10 +36,14 @@ function queryYielding(
3536 } ;
3637}
3738
39+ function driverWith ( options : CreateAgentSdkDriverOptions ) {
40+ return createAgentSdkCodingAgentDriver ( { listChangedFiles : async ( ) => [ ] , ...options } ) ;
41+ }
42+
3843test ( "success: session options, tool-use changed-file tracking, transcript, turn count" , async ( ) => {
3944 const captured : { input ?: Parameters < AgentSdkQueryFn > [ 0 ] } = { } ;
4045 const hooks = { PreToolUse : [ { hooks : [ "policy-callback" ] } ] } ;
41- const driver = createAgentSdkCodingAgentDriver ( {
46+ const driver = driverWith ( {
4247 query : queryYielding (
4348 [
4449 assistantMessage ( { type : "text" , text : "editing now" } ) ,
@@ -74,8 +79,59 @@ test("success: session options, tool-use changed-file tracking, transcript, turn
7479 assert . equal ( captured . input ! . options . hooks , hooks ) ;
7580} ) ;
7681
82+ test ( "success derives changed files from the worktree after untracked mutating tools" , async ( ) => {
83+ const driver = driverWith ( {
84+ query : queryYielding ( [
85+ assistantMessage ( { type : "tool_use" , name : "Bash" , input : { command : "node mutate.js" } } ) ,
86+ { type : "result" , subtype : "success" , is_error : false , num_turns : 2 , result : "mutated" } ,
87+ ] ) ,
88+ listChangedFiles : async ( cwd ) => {
89+ assert . equal ( cwd , task . workingDirectory ) ;
90+ return [ "packages/gittensory-engine/src/vulnerable.ts" ] ;
91+ } ,
92+ } ) ;
93+
94+ const result = await driver . run ( task ) ;
95+
96+ assert . equal ( result . ok , true ) ;
97+ assert . deepEqual ( result . changedFiles , [ "packages/gittensory-engine/src/vulnerable.ts" ] ) ;
98+ } ) ;
99+
100+ test ( "success fails closed when changed-file enumeration is unavailable" , async ( ) => {
101+ const driver = driverWith ( {
102+ query : queryYielding ( [
103+ { type : "result" , subtype : "success" , is_error : false , num_turns : 2 , result : "done" } ,
104+ ] ) ,
105+ listChangedFiles : async ( ) => {
106+ throw new Error ( "not a git worktree" ) ;
107+ } ,
108+ } ) ;
109+
110+ const result = await driver . run ( task ) ;
111+
112+ assert . equal ( result . ok , false ) ;
113+ assert . deepEqual ( result . changedFiles , [ ] ) ;
114+ assert . match ( result . error ! , / a g e n t _ s d k _ c h a n g e d _ f i l e s _ u n a v a i l a b l e : n o t a g i t w o r k t r e e / ) ;
115+ } ) ;
116+
117+ test ( "success stringifies a non-Error changed-file enumeration failure" , async ( ) => {
118+ const driver = driverWith ( {
119+ query : queryYielding ( [
120+ { type : "result" , subtype : "success" , is_error : false , num_turns : 2 , result : "done" } ,
121+ ] ) ,
122+ listChangedFiles : async ( ) => {
123+ throw "git unavailable" ;
124+ } ,
125+ } ) ;
126+
127+ const result = await driver . run ( task ) ;
128+
129+ assert . equal ( result . ok , false ) ;
130+ assert . equal ( result . error , "agent_sdk_changed_files_unavailable: git unavailable" ) ;
131+ } ) ;
132+
77133test ( "non-success result subtype maps to a structured failure with the subtype as the error" , async ( ) => {
78- const driver = createAgentSdkCodingAgentDriver ( {
134+ const driver = driverWith ( {
79135 query : queryYielding ( [
80136 assistantMessage ( { type : "tool_use" , name : "Edit" , input : { file_path : "src/a.ts" } } ) ,
81137 { type : "result" , subtype : "error_max_turns" , is_error : true , num_turns : 6 } ,
@@ -90,7 +146,7 @@ test("non-success result subtype maps to a structured failure with the subtype a
90146} ) ;
91147
92148test ( "a success-subtype result that still flags is_error is treated as a failure" , async ( ) => {
93- const driver = createAgentSdkCodingAgentDriver ( {
149+ const driver = driverWith ( {
94150 query : queryYielding ( [
95151 { type : "result" , subtype : "success" , is_error : true , num_turns : 1 , result : "refused" } ,
96152 ] ) ,
@@ -101,7 +157,7 @@ test("a success-subtype result that still flags is_error is treated as a failure
101157} ) ;
102158
103159test ( "stream ending without a result frame is a protocol failure, not a silent success" , async ( ) => {
104- const driver = createAgentSdkCodingAgentDriver ( {
160+ const driver = driverWith ( {
105161 query : queryYielding ( [ assistantMessage ( { type : "text" , text : "started..." } ) ] ) ,
106162 } ) ;
107163 const result = await driver . run ( task ) ;
@@ -111,7 +167,7 @@ test("stream ending without a result frame is a protocol failure, not a silent s
111167} ) ;
112168
113169test ( "a throw mid-stream returns a redacted structured failure and never propagates" , async ( ) => {
114- const driver = createAgentSdkCodingAgentDriver ( {
170+ const driver = driverWith ( {
115171 query : ( ) =>
116172 ( async function * ( ) : AsyncGenerator < Record < string , unknown > > {
117173 yield assistantMessage ( { type : "text" , text : "before the crash" } ) ;
@@ -127,7 +183,7 @@ test("a throw mid-stream returns a redacted structured failure and never propaga
127183} ) ;
128184
129185test ( "secret shapes in the result text are redacted from summary and transcript" , async ( ) => {
130- const driver = createAgentSdkCodingAgentDriver ( {
186+ const driver = driverWith ( {
131187 query : queryYielding ( [
132188 {
133189 type : "result" ,
@@ -146,7 +202,7 @@ test("secret shapes in the result text are redacted from summary and transcript"
146202} ) ;
147203
148204test ( "malformed frames (no content array, non-object blocks, missing file_path) are skipped defensively" , async ( ) => {
149- const driver = createAgentSdkCodingAgentDriver ( {
205+ const driver = driverWith ( {
150206 query : queryYielding ( [
151207 { type : "assistant" } ,
152208 { type : "assistant" , message : { content : "not-an-array" } } ,
@@ -167,7 +223,7 @@ test("malformed frames (no content array, non-object blocks, missing file_path)
167223} ) ;
168224
169225test ( "names a result frame with no usable subtype 'unknown'" , async ( ) => {
170- const driver = createAgentSdkCodingAgentDriver ( {
226+ const driver = driverWith ( {
171227 query : queryYielding ( [ { type : "result" , is_error : true } ] ) ,
172228 } ) ;
173229 const result = await driver . run ( task ) ;
0 commit comments