@@ -344,6 +344,78 @@ describe("config", async () => {
344344 }
345345} ) ;
346346
347+ test ( "buildStreamTextParams honors getGithubAppContext param" , async ( ) => {
348+ const mockGetGithubAppContext = mock ( ( ) =>
349+ Promise . resolve ( {
350+ appId : "custom-app-id" ,
351+ privateKey : "custom-private-key" ,
352+ } )
353+ ) ;
354+
355+ const agent = new blink . Agent < Message > ( ) ;
356+ const scout = new Scout ( {
357+ agent,
358+ logger : noopLogger ,
359+ github : {
360+ appID : "config-app-id" ,
361+ privateKey : "config-private-key" ,
362+ webhookSecret : "config-webhook-secret" ,
363+ } ,
364+ } ) ;
365+
366+ const params = scout . buildStreamTextParams ( {
367+ chatID : "test-chat-id" as blink . ID ,
368+ messages : [ ] ,
369+ model : newMockModel ( { textResponse : "test" } ) ,
370+ getGithubAppContext : mockGetGithubAppContext ,
371+ } ) ;
372+
373+ // Verify GitHub tools are available
374+ expect ( params . tools . github_create_pull_request ) . toBeDefined ( ) ;
375+
376+ const result = streamText ( params ) ;
377+
378+ // Access the tools from the streamText result
379+ // biome-ignore lint/suspicious/noExplicitAny: accessing internal tools for testing
380+ const tools = ( result as any ) . tools as Record <
381+ string ,
382+ // biome-ignore lint/suspicious/noExplicitAny: mock input
383+ { execute : ( input : any , opts ?: any ) => Promise < unknown > }
384+ > ;
385+
386+ // Execute a GitHub tool to verify our custom getGithubAppContext is called
387+ const tool = tools . github_create_pull_request ;
388+ expect ( tool ) . toBeDefined ( ) ;
389+
390+ // The tool will fail when trying to authenticate (since we're using fake credentials),
391+ // but we can verify our mock was called before that happens
392+ try {
393+ // biome-ignore lint/style/noNonNullAssertion: we just checked it's defined
394+ await tool ! . execute (
395+ {
396+ model_intent : "creating pull request" ,
397+ properties : {
398+ owner : "test-owner" ,
399+ repo : "test-repo" ,
400+ base : "main" ,
401+ head : "feature" ,
402+ title : "Test PR" ,
403+ } ,
404+ } ,
405+ {
406+ abortSignal : new AbortController ( ) . signal ,
407+ toolCallId : "test-tool-call" ,
408+ messages : [ ] ,
409+ }
410+ ) ;
411+ } catch {
412+ // Expected to fail during authentication
413+ }
414+
415+ // Verify our custom getGithubAppContext was called, not the default factory
416+ expect ( mockGetGithubAppContext ) . toHaveBeenCalledTimes ( 1 ) ;
417+ } ) ;
418+
347419test ( "respond in slack" , async ( ) => {
348420 const { promise : doStreamOptionsPromise , resolve } =
349421 newPromise < DoStreamOptions > ( ) ;
@@ -622,4 +694,96 @@ describe("daytona integration", () => {
622694 expect ( mockSandbox . getPreviewLink ) . toHaveBeenCalledTimes ( 1 ) ;
623695 expect ( mockSandbox . getPreviewLink ) . toHaveBeenCalledWith ( 2137 ) ;
624696 } ) ;
697+
698+ test ( "compute tools honor getGithubAppContext param" , async ( ) => {
699+ using apiServer = createMockBlinkApiServer ( ) ;
700+ using computeServer = createMockComputeServer ( ) ;
701+ using _env = withBlinkApiUrl ( apiServer . url ) ;
702+
703+ const mockGetGithubAppContext = mock ( ( ) =>
704+ Promise . resolve ( {
705+ appId : "custom-app-id" ,
706+ privateKey : "custom-private-key" ,
707+ } )
708+ ) ;
709+
710+ const mockSandbox = createMockDaytonaSandbox ( {
711+ id : "workspace-for-git-auth" ,
712+ getPreviewLink : mock ( ( ) =>
713+ Promise . resolve ( { url : computeServer . url , token : "test-token" } )
714+ ) ,
715+ } ) ;
716+ const mockSdk = createMockDaytonaSdk ( mockSandbox ) ;
717+
718+ const agent = new blink . Agent < Message > ( ) ;
719+ const scout = new Scout ( {
720+ agent,
721+ logger : noopLogger ,
722+ github : {
723+ appID : "config-app-id" ,
724+ privateKey : "config-private-key" ,
725+ webhookSecret : "config-webhook-secret" ,
726+ } ,
727+ compute : {
728+ type : "daytona" ,
729+ options : {
730+ apiKey : "test-api-key" ,
731+ computeServerPort : 2137 ,
732+ snapshot : "test-snapshot" ,
733+ daytonaSdk : mockSdk ,
734+ } ,
735+ } ,
736+ } ) ;
737+
738+ const params = scout . buildStreamTextParams ( {
739+ chatID : "test-chat-id" as blink . ID ,
740+ messages : [ ] ,
741+ model : newMockModel ( { textResponse : "test" } ) ,
742+ getGithubAppContext : mockGetGithubAppContext ,
743+ } ) ;
744+ const result = streamText ( params ) ;
745+
746+ // biome-ignore lint/suspicious/noExplicitAny: accessing internal tools for testing
747+ const tools = ( result as any ) . tools as Record <
748+ string ,
749+ // biome-ignore lint/suspicious/noExplicitAny: mock input
750+ { execute : ( input : any , opts ?: any ) => Promise < unknown > }
751+ > ;
752+
753+ // First, initialize the workspace (required before workspace_authenticate_git)
754+ // biome-ignore lint/style/noNonNullAssertion: we know it exists
755+ await tools . initialize_workspace ! . execute ( {
756+ model_intent : "initializing workspace" ,
757+ properties : { } ,
758+ } ) ;
759+
760+ // Verify workspace_authenticate_git tool is available
761+ const gitAuthTool = tools . workspace_authenticate_git ;
762+ expect ( gitAuthTool ) . toBeDefined ( ) ;
763+
764+ // Execute workspace_authenticate_git - it will fail when trying to authenticate
765+ // with GitHub (since we're using fake credentials), but our mock should be called first
766+ try {
767+ // biome-ignore lint/style/noNonNullAssertion: we just checked it's defined
768+ await gitAuthTool ! . execute (
769+ {
770+ model_intent : "authenticating git" ,
771+ properties : {
772+ owner : "test-owner" ,
773+ repos : [ "test-repo" ] ,
774+ } ,
775+ } ,
776+ {
777+ abortSignal : new AbortController ( ) . signal ,
778+ toolCallId : "git-auth-tool-call" ,
779+ messages : [ ] ,
780+ }
781+ ) ;
782+ } catch {
783+ // Expected to fail during GitHub authentication
784+ }
785+
786+ // Verify our custom getGithubAppContext was called, not the default factory
787+ expect ( mockGetGithubAppContext ) . toHaveBeenCalledTimes ( 1 ) ;
788+ } ) ;
625789} ) ;
0 commit comments