Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/scout-agent/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ agent.on("request", async (request) => {
});

agent.on("chat", async ({ id, messages }) => {
const params = scout.buildStreamTextParams({
const params = await scout.buildStreamTextParams({
messages,
chatID: id,
model: "anthropic/claude-sonnet-4.5",
Expand Down
15 changes: 4 additions & 11 deletions packages/scout-agent/lib/compute/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { WORKSPACE_INFO_KEY } from "./common";

export const createComputeTools = <T>({
agent,
getGithubAppContext,
githubAppContext,
initializeWorkspace,
createWorkspaceClient,
}: {
Expand All @@ -19,10 +19,10 @@ export const createComputeTools = <T>({
) => Promise<{ workspaceInfo: T; message: string }>;
createWorkspaceClient: (workspaceInfo: T) => Promise<Client>;
/**
* A function that returns the GitHub auth context for Git authentication.
* The GitHub auth context for Git authentication.
* If provided, the workspace_authenticate_git tool will be available.
*/
getGithubAppContext?: () => Promise<github.AppAuthOptions>;
githubAppContext?: github.AppAuthOptions;
}): Record<string, Tool> => {
const newClient = async () => {
const workspaceInfo = await agent.store.get(WORKSPACE_INFO_KEY);
Expand Down Expand Up @@ -56,7 +56,7 @@ export const createComputeTools = <T>({
},
}),

...(getGithubAppContext
...(githubAppContext
? {
workspace_authenticate_git: tool({
description: `Authenticate with Git repositories for push/pull operations. Call this before any Git operations that require authentication.
Expand All @@ -74,13 +74,6 @@ It's safe to call this multiple times - re-authenticating is perfectly fine and
execute: async (args, _opts) => {
const client = await newClient();

// Here we generate a GitHub token scoped to the repositories.
const githubAppContext = await getGithubAppContext();
if (!githubAppContext) {
throw new Error(
"You can only use public repositories in this context."
);
}
const token = await github.authenticateApp({
...githubAppContext,
// TODO: We obviously need to handle owner at some point.
Expand Down
10 changes: 5 additions & 5 deletions packages/scout-agent/lib/core.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ const newAgent = (options: {
return new Response("Hello, world!", { status: 200 });
});
agent.on("chat", async ({ messages }) => {
const params = core.buildStreamTextParams({
const params = await core.buildStreamTextParams({
model: options.model,
messages,
chatID: "b485db32-3d53-45fb-b980-6f4672fc66a6",
Expand Down Expand Up @@ -363,7 +363,7 @@ test("buildStreamTextParams honors getGithubAppContext param", async () => {
},
});

const params = scout.buildStreamTextParams({
const params = await scout.buildStreamTextParams({
chatID: "test-chat-id" as blink.ID,
messages: [],
model: newMockModel({ textResponse: "test" }),
Expand Down Expand Up @@ -576,7 +576,7 @@ describe("daytona integration", () => {
},
});

const params = scout.buildStreamTextParams({
const params = await scout.buildStreamTextParams({
chatID: "test-chat-id" as blink.ID,
messages: [],
model: newMockModel({ textResponse: "test" }),
Expand Down Expand Up @@ -643,7 +643,7 @@ describe("daytona integration", () => {
},
});

const params = scout.buildStreamTextParams({
const params = await scout.buildStreamTextParams({
chatID: "test-chat-id" as blink.ID,
messages: [],
model: newMockModel({ textResponse: "test" }),
Expand Down Expand Up @@ -735,7 +735,7 @@ describe("daytona integration", () => {
},
});

const params = scout.buildStreamTextParams({
const params = await scout.buildStreamTextParams({
chatID: "test-chat-id" as blink.ID,
messages: [],
model: newMockModel({ textResponse: "test" }),
Expand Down
43 changes: 18 additions & 25 deletions packages/scout-agent/lib/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export interface BuildStreamTextParamsOptions {
* A function that returns the GitHub auth context for the GitHub tools and for Git authentication inside workspaces.
* If not provided, the GitHub auth context will be created using the app ID and private key from the GitHub config.
*/
getGithubAppContext?: () => Promise<github.AppAuthOptions>;
getGithubAppContext?: () => Promise<github.AppAuthOptions | undefined>;
}

interface Logger {
Expand Down Expand Up @@ -264,23 +264,34 @@ export class Scout {
}
}

buildStreamTextParams({
async buildStreamTextParams({
messages,
chatID,
model,
providerOptions,
tools: providedTools,
getGithubAppContext,
systemPrompt = defaultSystemPrompt,
}: BuildStreamTextParamsOptions): {
}: BuildStreamTextParamsOptions): Promise<{
model: LanguageModel;
messages: ModelMessage[];
maxOutputTokens: number;
providerOptions?: ProviderOptions;
tools: Tools;
} {
}> {
this.printConfigWarnings();

// Resolve the GitHub app context once for all tools
const githubAppContext = this.github.config
? await (
getGithubAppContext ??
githubAppContextFactory({
appId: this.github.config.appID,
privateKey: this.github.config.privateKey,
})
)()
: undefined;

const slackMetadata = getSlackMetadata(messages);
const respondingInSlack =
this.slack.app !== undefined && slackMetadata !== undefined;
Expand All @@ -291,13 +302,7 @@ export class Scout {
case "docker": {
computeTools = createComputeTools<DockerWorkspaceInfo>({
agent: this.agent,
getGithubAppContext: this.github.config
? (getGithubAppContext ??
githubAppContextFactory({
appId: this.github.config.appID,
privateKey: this.github.config.privateKey,
}))
: undefined,
githubAppContext,
initializeWorkspace: initializeDockerWorkspace,
createWorkspaceClient: getDockerWorkspaceClient,
});
Expand All @@ -307,13 +312,7 @@ export class Scout {
const opts = computeConfig.options;
computeTools = createComputeTools<DaytonaWorkspaceInfo>({
agent: this.agent,
getGithubAppContext: this.github.config
? (getGithubAppContext ??
githubAppContextFactory({
appId: this.github.config.appID,
privateKey: this.github.config.privateKey,
}))
: undefined,
githubAppContext,
initializeWorkspace: (info) =>
initializeDaytonaWorkspace(
this.logger,
Expand Down Expand Up @@ -363,13 +362,7 @@ export class Scout {
? createGitHubTools({
agent: this.agent,
chatID,
getGithubAppContext:
getGithubAppContext !== undefined
? getGithubAppContext
: githubAppContextFactory({
appId: this.github.config.appID,
privateKey: this.github.config.privateKey,
}),
githubAppContext,
})
: undefined),
...computeTools,
Expand Down
19 changes: 9 additions & 10 deletions packages/scout-agent/lib/github.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,13 +187,12 @@ const withGitHubBotLogin = (login: string) => {
return withEnvVariable("GITHUB_BOT_LOGIN", login);
};

const getGithubAppContextFactory =
(args: { appId: string; privateKey: string }) => async () => {
return {
appId: args.appId,
privateKey: Buffer.from(args.privateKey, "base64").toString("utf-8"),
};
const makeGithubAppContext = (args: { appId: string; privateKey: string }) => {
return {
appId: args.appId,
privateKey: Buffer.from(args.privateKey, "base64").toString("utf-8"),
};
};

describe("defaultGetGithubAppContextFactory", () => {
test("decodes base64 private key", async () => {
Expand Down Expand Up @@ -248,7 +247,7 @@ describe("createGitHubTools", () => {
const tools = createGitHubTools({
agent,
chatID: "test-chat-id" as blink.ID,
getGithubAppContext: getGithubAppContextFactory({
githubAppContext: makeGithubAppContext({
appId: "app-id",
privateKey: Buffer.from("key").toString("base64"),
}),
Expand Down Expand Up @@ -317,7 +316,7 @@ describe("createGitHubTools", () => {
const tools = createGitHubTools({
agent,
chatID,
getGithubAppContext: getGithubAppContextFactory({
githubAppContext: makeGithubAppContext({
appId: "12345",
privateKey: TEST_RSA_PRIVATE_KEY_BASE64,
}),
Expand Down Expand Up @@ -407,7 +406,7 @@ describe("createGitHubTools", () => {
const tools = createGitHubTools({
agent,
chatID: "chat-id" as blink.ID,
getGithubAppContext: getGithubAppContextFactory({
githubAppContext: makeGithubAppContext({
appId: "12345",
privateKey: TEST_RSA_PRIVATE_KEY_BASE64,
}),
Expand Down Expand Up @@ -508,7 +507,7 @@ describe("createGitHubTools", () => {
const tools = createGitHubTools({
agent,
chatID: "chat" as blink.ID,
getGithubAppContext: getGithubAppContextFactory({
githubAppContext: makeGithubAppContext({
appId: "12345",
privateKey: TEST_RSA_PRIVATE_KEY_BASE64,
}),
Expand Down
13 changes: 7 additions & 6 deletions packages/scout-agent/lib/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,26 @@ export const githubAppContextFactory = ({
export const createGitHubTools = ({
agent,
chatID,
getGithubAppContext,
githubAppContext,
}: {
agent: blink.Agent<UIMessage>;
chatID: blink.ID;
getGithubAppContext: () => Promise<github.AppAuthOptions>;
githubAppContext: github.AppAuthOptions | undefined;
}): Record<string, Tool> => {
return {
...blink.tools.prefix(
blink.tools.withContext(github.tools, {
appAuth: getGithubAppContext,
}),
githubAppContext
? blink.tools.withContext(github.tools, {
appAuth: githubAppContext,
})
: github.tools,
"github_"
),

github_create_pull_request: tool({
description: github.tools.create_pull_request.description,
inputSchema: github.tools.create_pull_request.inputSchema,
execute: async (args, { abortSignal }) => {
const githubAppContext = await getGithubAppContext();
if (!githubAppContext) {
throw new Error(
"You are not authorized to use this tool in this context."
Expand Down
2 changes: 1 addition & 1 deletion packages/scout-agent/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@blink-sdk/scout-agent",
"description": "A general-purpose AI agent with GitHub, Slack, web search, and compute capabilities built on Blink SDK.",
"version": "0.0.5",
"version": "0.0.6",
"type": "module",
"keywords": [
"blink",
Expand Down