@@ -21,6 +21,7 @@ import (
2121 "github.com/stretchr/testify/require"
2222
2323 "github.com/fullsend-ai/fullsend/internal/binary"
24+ "github.com/fullsend-ai/fullsend/internal/config"
2425 "github.com/fullsend-ai/fullsend/internal/fetch"
2526 "github.com/fullsend-ai/fullsend/internal/fetchsvc"
2627 "github.com/fullsend-ai/fullsend/internal/forge"
@@ -478,6 +479,209 @@ func TestRunCommand_HasEnvFileFlag(t *testing.T) {
478479 assert .Equal (t , []string {"/tmp/a.env" , "/tmp/b.env" }, val )
479480}
480481
482+ func TestRunAgent_ConfigAgentLocalPath (t * testing.T ) {
483+ dir := t .TempDir ()
484+ require .NoError (t , os .MkdirAll (filepath .Join (dir , "harness" ), 0o755 ))
485+ require .NoError (t , os .MkdirAll (filepath .Join (dir , "agents" ), 0o755 ))
486+
487+ require .NoError (t , os .WriteFile (
488+ filepath .Join (dir , "agents" , "custom.md" ),
489+ []byte ("You are a custom agent." ),
490+ 0o644 ,
491+ ))
492+ require .NoError (t , os .WriteFile (
493+ filepath .Join (dir , "harness" , "custom.yaml" ),
494+ []byte ("agent: agents/custom.md\n role: test\n " ),
495+ 0o644 ,
496+ ))
497+ require .NoError (t , os .WriteFile (
498+ filepath .Join (dir , "config.yaml" ),
499+ []byte ("agents:\n - harness/custom.yaml\n allowed_remote_resources:\n - \" https://example.com/\" \n " ),
500+ 0o644 ,
501+ ))
502+
503+ rFlags := resolveFlags {maxDepth : 10 , maxResources : 50 }
504+ printer := ui .New (io .Discard )
505+ repoDir := t .TempDir ()
506+ err := runAgent (context .Background (), "custom" , dir , "" , repoDir , "" , nil , false , "" , "" , rFlags , statusOpts {}, printer , false )
507+ require .Error (t , err )
508+ assert .Contains (t , err .Error (), "openshell" )
509+ }
510+
511+ func TestRunAgent_ConfigAgentURL (t * testing.T ) {
512+ harnessContent := []byte ("agent: agents/remote.md\n role: test\n " )
513+ harnessHash := fetch .ComputeSHA256 (harnessContent )
514+
515+ srv , policy := newLockTestServer (t , map [string ][]byte {
516+ "/harness/triage.yaml" : harnessContent ,
517+ })
518+
519+ dir := t .TempDir ()
520+ require .NoError (t , os .MkdirAll (filepath .Join (dir , "harness" ), 0o755 ))
521+ require .NoError (t , os .MkdirAll (filepath .Join (dir , "agents" ), 0o755 ))
522+
523+ require .NoError (t , os .WriteFile (
524+ filepath .Join (dir , "agents" , "remote.md" ),
525+ []byte ("You are a remote agent." ),
526+ 0o644 ,
527+ ))
528+ require .NoError (t , os .WriteFile (
529+ filepath .Join (dir , "config.yaml" ),
530+ []byte (fmt .Sprintf ("agents:\n - \" %s/harness/triage.yaml#sha256=%s\" \n allowed_remote_resources:\n - \" %s/\" \n " , srv .URL , harnessHash , srv .URL )),
531+ 0o644 ,
532+ ))
533+
534+ fetch .DefaultPolicy = policy
535+ defer func () { fetch .DefaultPolicy = fetch.FetchPolicy {} }()
536+
537+ rFlags := resolveFlags {maxDepth : 10 , maxResources : 50 }
538+ printer := ui .New (io .Discard )
539+ repoDir := t .TempDir ()
540+ err := runAgent (context .Background (), "triage" , dir , "" , repoDir , "" , nil , false , "" , "" , rFlags , statusOpts {}, printer , false )
541+ require .Error (t , err )
542+ assert .Contains (t , err .Error (), "openshell" )
543+ }
544+
545+ func TestRunAgent_ConfigAgentOverridesScaffold (t * testing.T ) {
546+ // When config has an agent with the same name as a scaffold agent,
547+ // the config source is used instead of the scaffold wrapper.
548+ dir := t .TempDir ()
549+ require .NoError (t , os .MkdirAll (filepath .Join (dir , "harness" ), 0o755 ))
550+ require .NoError (t , os .MkdirAll (filepath .Join (dir , "agents" ), 0o755 ))
551+
552+ require .NoError (t , os .WriteFile (
553+ filepath .Join (dir , "agents" , "code.md" ),
554+ []byte ("You are a custom code agent." ),
555+ 0o644 ,
556+ ))
557+ // Config-driven local path agent named "code" — should take precedence
558+ // over any scaffold "code" harness wrapper.
559+ require .NoError (t , os .WriteFile (
560+ filepath .Join (dir , "harness" , "code.yaml" ),
561+ []byte ("agent: agents/code.md\n role: test\n " ),
562+ 0o644 ,
563+ ))
564+ require .NoError (t , os .WriteFile (
565+ filepath .Join (dir , "config.yaml" ),
566+ []byte ("agents:\n - harness/code.yaml\n allowed_remote_resources:\n - \" https://example.com/\" \n " ),
567+ 0o644 ,
568+ ))
569+
570+ rFlags := resolveFlags {maxDepth : 10 , maxResources : 50 }
571+ printer := ui .New (io .Discard )
572+ repoDir := t .TempDir ()
573+ err := runAgent (context .Background (), "code" , dir , "" , repoDir , "" , nil , false , "" , "" , rFlags , statusOpts {}, printer , false )
574+ require .Error (t , err )
575+ assert .Contains (t , err .Error (), "openshell" )
576+ }
577+
578+ func TestRunAgent_ScaffoldFallback (t * testing.T ) {
579+ // When config has agents but the requested agent is not in config,
580+ // fall back to disk-based resolution.
581+ dir := t .TempDir ()
582+ require .NoError (t , os .MkdirAll (filepath .Join (dir , "harness" ), 0o755 ))
583+ require .NoError (t , os .MkdirAll (filepath .Join (dir , "agents" ), 0o755 ))
584+
585+ require .NoError (t , os .WriteFile (
586+ filepath .Join (dir , "agents" , "code.md" ),
587+ []byte ("You are a coding agent." ),
588+ 0o644 ,
589+ ))
590+ require .NoError (t , os .WriteFile (
591+ filepath .Join (dir , "harness" , "code.yaml" ),
592+ []byte ("agent: agents/code.md\n role: test\n " ),
593+ 0o644 ,
594+ ))
595+ // Config has agents but "code" is not among them — should fall back to disk.
596+ require .NoError (t , os .WriteFile (
597+ filepath .Join (dir , "config.yaml" ),
598+ []byte ("agents:\n - harness/other.yaml\n allowed_remote_resources:\n - \" https://example.com/\" \n " ),
599+ 0o644 ,
600+ ))
601+
602+ rFlags := resolveFlags {maxDepth : 10 , maxResources : 50 }
603+ printer := ui .New (io .Discard )
604+ repoDir := t .TempDir ()
605+ err := runAgent (context .Background (), "code" , dir , "" , repoDir , "" , nil , false , "" , "" , rFlags , statusOpts {}, printer , false )
606+ require .Error (t , err )
607+ assert .Contains (t , err .Error (), "openshell" )
608+ }
609+
610+ func TestRunAgent_UnknownAgentName (t * testing.T ) {
611+ dir := t .TempDir ()
612+ require .NoError (t , os .MkdirAll (filepath .Join (dir , "harness" ), 0o755 ))
613+
614+ // Config has agents but "nonexistent" is not among them, and no file on disk.
615+ require .NoError (t , os .WriteFile (
616+ filepath .Join (dir , "config.yaml" ),
617+ []byte ("agents:\n - harness/other.yaml\n allowed_remote_resources:\n - \" https://example.com/\" \n " ),
618+ 0o644 ,
619+ ))
620+
621+ rFlags := resolveFlags {maxDepth : 10 , maxResources : 50 }
622+ printer := ui .New (io .Discard )
623+ repoDir := t .TempDir ()
624+ err := runAgent (context .Background (), "nonexistent" , dir , "" , repoDir , "" , nil , false , "" , "" , rFlags , statusOpts {}, printer , false )
625+ require .Error (t , err )
626+ assert .Contains (t , err .Error (), "harness file not found" )
627+ }
628+
629+ func TestResolveAgentSource_NoConfig (t * testing.T ) {
630+ dir := t .TempDir ()
631+ require .NoError (t , os .MkdirAll (filepath .Join (dir , "harness" ), 0o755 ))
632+ require .NoError (t , os .WriteFile (
633+ filepath .Join (dir , "harness" , "code.yaml" ),
634+ []byte ("agent: agents/code.md\n role: test\n " ),
635+ 0o644 ,
636+ ))
637+
638+ printer := ui .New (io .Discard )
639+ path , deps , err := resolveAgentSource (context .Background (), dir , "code" , nil , harness.ComposeOpts {}, printer )
640+ require .NoError (t , err )
641+ assert .Contains (t , path , "code.yaml" )
642+ assert .Empty (t , deps )
643+ }
644+
645+ func TestResolveAgentSource_ConfigLocalPath (t * testing.T ) {
646+ dir := t .TempDir ()
647+ require .NoError (t , os .MkdirAll (filepath .Join (dir , "harness" ), 0o755 ))
648+ require .NoError (t , os .WriteFile (
649+ filepath .Join (dir , "harness" , "custom.yaml" ),
650+ []byte ("agent: agents/custom.md\n role: test\n " ),
651+ 0o644 ,
652+ ))
653+
654+ orgCfg := & config.OrgConfig {
655+ Agents : []config.AgentEntry {
656+ {Source : "harness/custom.yaml" },
657+ },
658+ AllowedRemoteResources : []string {"https://example.com/" },
659+ }
660+
661+ printer := ui .New (io .Discard )
662+ path , deps , err := resolveAgentSource (context .Background (), dir , "custom" , orgCfg , harness.ComposeOpts {}, printer )
663+ require .NoError (t , err )
664+ assert .Equal (t , filepath .Join (dir , "harness" , "custom.yaml" ), path )
665+ assert .Empty (t , deps )
666+ }
667+
668+ func TestResolveAgentSource_ConfigLocalPathNotFound (t * testing.T ) {
669+ dir := t .TempDir ()
670+ require .NoError (t , os .MkdirAll (filepath .Join (dir , "harness" ), 0o755 ))
671+
672+ orgCfg := & config.OrgConfig {
673+ Agents : []config.AgentEntry {
674+ {Source : "harness/missing.yaml" },
675+ },
676+ AllowedRemoteResources : []string {"https://example.com/" },
677+ }
678+
679+ printer := ui .New (io .Discard )
680+ _ , _ , err := resolveAgentSource (context .Background (), dir , "missing" , orgCfg , harness.ComposeOpts {}, printer )
681+ require .Error (t , err )
682+ assert .Contains (t , err .Error (), "config agent missing" )
683+ }
684+
481685func TestApplySandboxImageOverride_Applied (t * testing.T ) {
482686 t .Setenv ("FULLSEND_SANDBOX_IMAGE" , "ghcr.io/fullsend-ai/fullsend-sandbox:dev" )
483687
0 commit comments