github-copilot-sdk 1.0.5-preview.1

Rust SDK for programmatic control of the GitHub Copilot CLI via JSON-RPC.
Documentation
use std::sync::Arc;

use async_trait::async_trait;
use github_copilot_sdk::hooks::{
    ErrorOccurredInput, ErrorOccurredOutput, HookContext, PostToolUseFailureInput,
    PostToolUseFailureOutput, PostToolUseInput, PostToolUseOutput, PreToolUseInput,
    PreToolUseOutput, SessionEndInput, SessionEndOutput, SessionHooks, SessionStartInput,
    SessionStartOutput, UserPromptSubmittedInput, UserPromptSubmittedOutput,
};

use super::support::{assert_unsupported_hooks_error, with_e2e_context};

#[tokio::test]
async fn rejects_extended_sdk_callback_hooks() {
    with_e2e_context(
        "hooks_extended",
        "rejects_extended_sdk_callback_hooks",
        |ctx| {
            Box::pin(async move {
                ctx.set_default_copilot_user();
                let client = ctx.start_client().await;
                match client
                    .create_session(
                        ctx.approve_all_session_config()
                            .with_hooks(Arc::new(ExtendedHooks)),
                    )
                    .await
                {
                    Ok(session) => {
                        session.disconnect().await.expect("disconnect session");
                        panic!("expected SDK callback hooks to be rejected");
                    }
                    Err(err) => assert_unsupported_hooks_error(err),
                }
                client.stop().await.expect("stop client");
            })
        },
    )
    .await;
}

struct ExtendedHooks;

#[async_trait]
impl SessionHooks for ExtendedHooks {
    async fn on_user_prompt_submitted(
        &self,
        _input: UserPromptSubmittedInput,
        _ctx: HookContext,
    ) -> Option<UserPromptSubmittedOutput> {
        Some(UserPromptSubmittedOutput {
            modified_prompt: Some("not used".to_string()),
            ..UserPromptSubmittedOutput::default()
        })
    }

    async fn on_session_start(
        &self,
        _input: SessionStartInput,
        _ctx: HookContext,
    ) -> Option<SessionStartOutput> {
        Some(SessionStartOutput {
            additional_context: Some("not used".to_string()),
            ..SessionStartOutput::default()
        })
    }

    async fn on_session_end(
        &self,
        _input: SessionEndInput,
        _ctx: HookContext,
    ) -> Option<SessionEndOutput> {
        Some(SessionEndOutput {
            session_summary: Some("not used".to_string()),
            ..SessionEndOutput::default()
        })
    }

    async fn on_error_occurred(
        &self,
        _input: ErrorOccurredInput,
        _ctx: HookContext,
    ) -> Option<ErrorOccurredOutput> {
        Some(ErrorOccurredOutput {
            error_handling: Some("skip".to_string()),
            ..ErrorOccurredOutput::default()
        })
    }

    async fn on_pre_tool_use(
        &self,
        _input: PreToolUseInput,
        _ctx: HookContext,
    ) -> Option<PreToolUseOutput> {
        Some(PreToolUseOutput {
            permission_decision: Some("allow".to_string()),
            ..PreToolUseOutput::default()
        })
    }

    async fn on_post_tool_use(
        &self,
        _input: PostToolUseInput,
        _ctx: HookContext,
    ) -> Option<PostToolUseOutput> {
        Some(PostToolUseOutput {
            suppress_output: Some(false),
            ..PostToolUseOutput::default()
        })
    }

    async fn on_post_tool_use_failure(
        &self,
        _input: PostToolUseFailureInput,
        _ctx: HookContext,
    ) -> Option<PostToolUseFailureOutput> {
        Some(PostToolUseFailureOutput {
            additional_context: Some("not used".to_string()),
        })
    }
}