Skip to content

Commit baad1e2

Browse files
committed
Merge branch 'main' into gemini-streamsse
2 parents f72199d + 7b9cb65 commit baad1e2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+3706
-1856
lines changed

.github/workflows/continue-general-review.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name: Continue General Review
33
on:
44
push:
55
branches:
6-
- nate/fix-wf
6+
- main
77
pull_request:
88
types: [opened, ready_for_review]
99
issue_comment:
@@ -25,4 +25,4 @@ jobs:
2525
with:
2626
continue-api-key: ${{ secrets.CONTINUE_API_KEY }}
2727
continue-org: "continuedev"
28-
continue-config: "continuedev/review-bot"
28+
continue-agent: "empty-agent"

.github/workflows/run-continue-agent.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ on:
77
description: "The prompt to send to the Continue agent"
88
required: true
99
type: string
10-
agent:
11-
description: "The agent to use (e.g., continuedev/default-background-agent)"
10+
config:
11+
description: "The config to use (e.g., continuedev/default-background-agent)"
1212
required: false
1313
type: string
1414
default: "continuedev/default-background-agent"
@@ -33,7 +33,7 @@ jobs:
3333
-H "Authorization: Bearer ${{ secrets.CONTINUE_API_KEY }}" \
3434
-d '{
3535
"prompt": "${{ inputs.prompt }}",
36-
"agent": "${{ inputs.agent }}",
36+
"config": "${{ inputs.config }}",
3737
"branchName": "${{ inputs.branch_name }}",
3838
"repoUrl": "https://github.com/${{ github.repository }}"
3939
}')

.github/workflows/snyk-agent.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
fetch-depth: 0
1818

1919
- name: Setup Node.js
20-
uses: actions/setup-node@v5
20+
uses: actions/setup-node@v6
2121
with:
2222
node-version: "20"
2323

.github/workflows/tidy-up-codebase.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
uses: ./.github/workflows/run-continue-agent.yml
1212
with:
1313
prompt: "Review every Markdown documentation file and verify that descriptions, examples, or behavior outlines accurately reflect the current code. Only update documentation; do not modify code. Check the corresponding code to confirm behavior before making changes. Correct any inaccuracies or outdated information in descriptions, examples, or behavior outlines. Preserve existing Markdown formatting, style, and structure. Do not add new sections, speculative explanations, or details not present in the code. Only update statements that are clearly incorrect or misleading; do not rewrite text for style or preference. Keep edits minimal and focused, ensuring that the Markdown matches what the code actually does. If verification against the code is ambiguous, leave the documentation unchanged. Use branch name bot/cleanup-<YYMMDD>-<HHMM>"
14-
agent: continuedev/default-background-agent
14+
config: continuedev/default-background-agent
1515
branch_name: main
1616
secrets:
1717
CONTINUE_API_KEY: ${{ secrets.CONTINUE_API_KEY }}

actions/general-review/action.yml

Lines changed: 89 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ inputs:
99
continue-org:
1010
description: "Organization for Continue config"
1111
required: true
12-
continue-config:
13-
description: 'Config path to use (e.g., "myorg/review-bot")'
12+
continue-agent:
13+
description: 'Agent path to use (e.g., "myorg/review-bot")'
1414
required: true
1515

1616
runs:
@@ -20,44 +20,89 @@ runs:
2020
uses: actions/checkout@v4
2121

2222
- name: Check Authorization
23-
shell: bash
24-
env:
25-
# Only move the dangerous check to env to prevent shell injection
26-
HAS_TRIGGER_PHRASE: ${{ contains(github.event.comment.body, '@continue-review') }}
27-
run: |
28-
# Check if this action should run based on event type and user permissions
29-
SHOULD_RUN="false"
23+
id: auth-check
24+
uses: actions/github-script@v7
25+
with:
26+
script: |
27+
let shouldRun = false;
28+
let skipReason = '';
3029
31-
if [ "${{ github.event_name }}" = "pull_request" ]; then
32-
# Check if PR is a draft
33-
if [ "${{ github.event.pull_request.draft }}" = "true" ]; then
34-
echo "::notice::Skipping review - PR is a draft"
35-
else
36-
# Check PR author association
37-
AUTHOR_ASSOC="${{ github.event.pull_request.author_association }}"
38-
if [ "$AUTHOR_ASSOC" = "OWNER" ] || [ "$AUTHOR_ASSOC" = "MEMBER" ] || [ "$AUTHOR_ASSOC" = "COLLABORATOR" ]; then
39-
SHOULD_RUN="true"
40-
else
41-
echo "::notice::Skipping review - PR author is not a team member (association: $AUTHOR_ASSOC)"
42-
fi
43-
fi
44-
elif [ "${{ github.event_name }}" = "issue_comment" ]; then
45-
# Check if it's a PR comment with the trigger phrase
46-
if [ "${{ github.event.issue.pull_request }}" != "" ] && [ "$HAS_TRIGGER_PHRASE" = "true" ]; then
47-
COMMENTER_ASSOC="${{ github.event.comment.author_association }}"
48-
if [ "$COMMENTER_ASSOC" = "OWNER" ] || [ "$COMMENTER_ASSOC" = "MEMBER" ] || [ "$COMMENTER_ASSOC" = "COLLABORATOR" ]; then
49-
SHOULD_RUN="true"
50-
else
51-
echo "::notice::Skipping review - Commenter is not a team member (association: $COMMENTER_ASSOC)"
52-
fi
53-
else
54-
echo "::notice::Skipping review - Comment does not contain @continue-review trigger, or is not on a PR"
55-
fi
56-
else
57-
echo "::notice::Skipping review - Unsupported event type: ${{ github.event_name }}"
58-
fi
30+
if (context.eventName === 'pull_request') {
31+
// Check if PR is a draft
32+
if (context.payload.pull_request.draft) {
33+
skipReason = 'PR is a draft';
34+
} else {
35+
// Check if user has write permission (includes admin, maintain, write)
36+
const prAuthor = context.payload.pull_request.user.login;
37+
try {
38+
const { data: permission } = await github.rest.repos.getCollaboratorPermissionLevel({
39+
owner: context.repo.owner,
40+
repo: context.repo.repo,
41+
username: prAuthor
42+
});
43+
44+
const allowedPermissions = ['admin', 'maintain', 'write'];
45+
if (allowedPermissions.includes(permission.permission)) {
46+
shouldRun = true;
47+
console.log(`PR author @${prAuthor} has ${permission.permission} permission`);
48+
} else {
49+
skipReason = `PR author @${prAuthor} does not have write permission (has: ${permission.permission})`;
50+
}
51+
} catch (error) {
52+
// If API call fails, fall back to checking author_association
53+
const association = context.payload.pull_request.author_association;
54+
const allowedAssociations = ['OWNER', 'MEMBER', 'COLLABORATOR'];
55+
if (allowedAssociations.includes(association)) {
56+
shouldRun = true;
57+
console.log(`PR author @${prAuthor} association: ${association}`);
58+
} else {
59+
skipReason = `PR author @${prAuthor} is not a team member (association: ${association})`;
60+
}
61+
}
62+
}
63+
} else if (context.eventName === 'issue_comment') {
64+
// Check if it's a PR comment with the trigger phrase
65+
const hasTrigger = context.payload.comment.body.includes('@continue-review');
66+
if (context.payload.issue.pull_request && hasTrigger) {
67+
const commenter = context.payload.comment.user.login;
68+
try {
69+
const { data: permission } = await github.rest.repos.getCollaboratorPermissionLevel({
70+
owner: context.repo.owner,
71+
repo: context.repo.repo,
72+
username: commenter
73+
});
74+
75+
const allowedPermissions = ['admin', 'maintain', 'write'];
76+
if (allowedPermissions.includes(permission.permission)) {
77+
shouldRun = true;
78+
console.log(`Commenter @${commenter} has ${permission.permission} permission`);
79+
} else {
80+
skipReason = `Commenter @${commenter} does not have write permission (has: ${permission.permission})`;
81+
}
82+
} catch (error) {
83+
// If API call fails, fall back to checking author_association
84+
const association = context.payload.comment.author_association;
85+
const allowedAssociations = ['OWNER', 'MEMBER', 'COLLABORATOR'];
86+
if (allowedAssociations.includes(association)) {
87+
shouldRun = true;
88+
console.log(`Commenter @${commenter} association: ${association}`);
89+
} else {
90+
skipReason = `Commenter @${commenter} is not a team member (association: ${association})`;
91+
}
92+
}
93+
} else {
94+
skipReason = 'Comment does not contain @continue-review trigger, or is not on a PR';
95+
}
96+
} else {
97+
skipReason = `Unsupported event type: ${context.eventName}`;
98+
}
99+
100+
if (skipReason) {
101+
core.notice(`Skipping review - ${skipReason}`);
102+
}
59103
60-
echo "SHOULD_RUN=$SHOULD_RUN" >> $GITHUB_ENV
104+
core.exportVariable('SHOULD_RUN', shouldRun.toString());
105+
return shouldRun;
61106
62107
- name: Setup Node.js
63108
if: env.SHOULD_RUN == 'true'
@@ -68,7 +113,7 @@ runs:
68113
- name: Install Continue CLI
69114
if: env.SHOULD_RUN == 'true'
70115
shell: bash
71-
run: npm install -g @continuedev/cli@1.4.30
116+
run: npm install -g @continuedev/cli@latest
72117

73118
- name: Post Initial Comment
74119
if: env.SHOULD_RUN == 'true'
@@ -182,7 +227,7 @@ runs:
182227
env:
183228
CONTINUE_API_KEY: ${{ inputs.continue-api-key }}
184229
CONTINUE_ORG: ${{ inputs.continue-org }}
185-
CONTINUE_CONFIG: ${{ inputs.continue-config }}
230+
CONTINUE_AGENT: ${{ inputs.continue-agent }}
186231
GITHUB_TOKEN: ${{ github.token }}
187232
run: |
188233
echo "Running Continue CLI with prompt:"
@@ -208,7 +253,7 @@ runs:
208253
exit 1
209254
fi
210255
211-
if [[ ! "$CONTINUE_CONFIG" =~ ^[a-zA-Z0-9_/-]+$ ]]; then
256+
if [[ ! "$CONTINUE_AGENT" =~ ^[a-zA-Z0-9_/-]+$ ]]; then
212257
echo "Error: Invalid config path. Must contain only alphanumeric characters, hyphens, underscores, and forward slashes."
213258
exit 1
214259
fi
@@ -228,7 +273,7 @@ runs:
228273
229274
# Run the CLI with validated config and error handling
230275
if [ "$SKIP_CLI" != "true" ]; then
231-
echo "Executing Continue CLI with config: $CONTINUE_ORG/$CONTINUE_CONFIG"
276+
echo "Executing Continue CLI with config: $CONTINUE_ORG/$CONTINUE_AGENT"
232277
233278
# Write prompt to temp file for headless mode
234279
PROMPT_FILE="/tmp/continue-review-$RANDOM.txt"
@@ -237,9 +282,9 @@ runs:
237282
echo "Prompt length: $(wc -c < "$PROMPT_FILE") characters"
238283
239284
# Use timeout to prevent hanging (360 seconds = 6 minutes)
240-
echo "Executing command: cn --config $CONTINUE_ORG/$CONTINUE_CONFIG -p @$PROMPT_FILE --allow Bash"
285+
echo "Executing command: cn --agent $CONTINUE_ORG/$CONTINUE_AGENT -p @$PROMPT_FILE --allow Bash"
241286
242-
if timeout 360 cn --config "$CONTINUE_ORG/$CONTINUE_CONFIG" -p "@$PROMPT_FILE" --allow Bash > code_review_raw.md 2>cli_error.log; then
287+
if timeout 360 cn --agent "$CONTINUE_ORG/$CONTINUE_AGENT" -p "@$PROMPT_FILE" --allow Bash > code_review_raw.md 2>cli_error.log; then
243288
echo "Continue CLI completed successfully"
244289
echo "Raw output length: $(wc -c < code_review_raw.md) characters"
245290

core/control-plane/client.ts

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,4 +439,138 @@ export class ControlPlaneClient {
439439
);
440440
}
441441
}
442+
443+
/**
444+
* Create a new background agent
445+
*/
446+
public async createBackgroundAgent(
447+
prompt: string,
448+
repoUrl: string,
449+
name: string,
450+
branch?: string,
451+
organizationId?: string,
452+
contextItems?: any[],
453+
selectedCode?: any[],
454+
agent?: string,
455+
): Promise<{ id: string }> {
456+
if (!(await this.isSignedIn())) {
457+
throw new Error("Not signed in to Continue");
458+
}
459+
460+
const requestBody: any = {
461+
prompt,
462+
repoUrl,
463+
name,
464+
branchName: branch,
465+
};
466+
467+
if (organizationId) {
468+
requestBody.organizationId = organizationId;
469+
}
470+
471+
// Include context items if provided
472+
if (contextItems && contextItems.length > 0) {
473+
requestBody.contextItems = contextItems.map((item) => ({
474+
content: item.content,
475+
description: item.description,
476+
name: item.name,
477+
uri: item.uri,
478+
}));
479+
}
480+
481+
// Include selected code if provided
482+
if (selectedCode && selectedCode.length > 0) {
483+
requestBody.selectedCode = selectedCode.map((code) => ({
484+
filepath: code.filepath,
485+
range: code.range,
486+
contents: code.contents,
487+
}));
488+
}
489+
490+
// Include agent configuration if provided
491+
if (agent) {
492+
requestBody.agent = agent;
493+
}
494+
495+
const resp = await this.requestAndHandleError("agents", {
496+
method: "POST",
497+
headers: {
498+
"Content-Type": "application/json",
499+
},
500+
body: JSON.stringify(requestBody),
501+
});
502+
503+
return (await resp.json()) as { id: string };
504+
}
505+
506+
/**
507+
* List all background agents for the current user or organization
508+
* @param organizationId - Optional organization ID to filter agents by organization scope
509+
* @param limit - Optional limit for number of agents to return (default: 5)
510+
*/
511+
public async listBackgroundAgents(
512+
organizationId?: string,
513+
limit?: number,
514+
): Promise<{
515+
agents: Array<{
516+
id: string;
517+
name: string | null;
518+
status: string;
519+
repoUrl: string;
520+
createdAt: string;
521+
metadata?: {
522+
github_repo?: string;
523+
};
524+
}>;
525+
totalCount: number;
526+
}> {
527+
if (!(await this.isSignedIn())) {
528+
return { agents: [], totalCount: 0 };
529+
}
530+
531+
try {
532+
// Build URL with query parameters
533+
const params = new URLSearchParams();
534+
if (organizationId) {
535+
params.set("organizationId", organizationId);
536+
}
537+
if (limit !== undefined) {
538+
params.set("limit", limit.toString());
539+
}
540+
541+
const url = `agents${params.toString() ? `?${params.toString()}` : ""}`;
542+
543+
const resp = await this.requestAndHandleError(url, {
544+
method: "GET",
545+
});
546+
547+
const result = (await resp.json()) as {
548+
agents: any[];
549+
totalCount: number;
550+
};
551+
552+
return {
553+
agents: result.agents.map((agent: any) => ({
554+
id: agent.id,
555+
name: agent.name || agent.metadata?.name || null,
556+
status: agent.status,
557+
repoUrl: agent.metadata?.repo_url || agent.repo_url || "",
558+
createdAt:
559+
agent.created_at || agent.create_time_ms
560+
? new Date(agent.created_at || agent.create_time_ms).toISOString()
561+
: new Date().toISOString(),
562+
metadata: {
563+
github_repo:
564+
agent.metadata?.github_repo || agent.metadata?.repo_url,
565+
},
566+
})),
567+
totalCount: result.totalCount,
568+
};
569+
} catch (e) {
570+
Logger.error(e, {
571+
context: "control_plane_list_background_agents",
572+
});
573+
return { agents: [], totalCount: 0 };
574+
}
575+
}
442576
}

core/core.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -472,9 +472,11 @@ export class Core {
472472
const urlPath = msg.data.path.startsWith("/")
473473
? msg.data.path.slice(1)
474474
: msg.data.path;
475-
let url = `${env.APP_URL}${urlPath}`;
475+
let url;
476476
if (msg.data.orgSlug) {
477-
url += `?org=${msg.data.orgSlug}`;
477+
url = `${env.APP_URL}organizations/${msg.data.orgSlug}/${urlPath}`;
478+
} else {
479+
url = `${env.APP_URL}${urlPath}`;
478480
}
479481
await this.messenger.request("openUrl", url);
480482
});

core/index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ export interface PromptLog {
462462
completion: string;
463463
}
464464

465-
export type MessageModes = "chat" | "agent" | "plan";
465+
export type MessageModes = "chat" | "agent" | "plan" | "background";
466466

467467
export type ToolStatus =
468468
| "generating" // Tool call arguments are being streamed from the LLM

0 commit comments

Comments
 (0)