Skip to content

Commit ac4e694

Browse files
Merge branch 'main' into bkellam/agent-improvements
2 parents 9a3ea1c + ee74234 commit ac4e694

File tree

15 files changed

+327
-157
lines changed

15 files changed

+327
-157
lines changed

.github/workflows/license-audit.yml

Lines changed: 70 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ on:
1010
- "scripts/fetchLicenses.mjs"
1111
- "scripts/summarizeLicenses.mjs"
1212
- "scripts/npmLicenseMap.json"
13+
- "scripts/licenseAuditPrompt.txt"
14+
- "scripts/runLicenseAudit.sh"
1315
workflow_dispatch:
1416

1517
jobs:
@@ -40,80 +42,21 @@ jobs:
4042
- name: Summarize licenses
4143
run: node scripts/summarizeLicenses.mjs
4244

45+
- name: Read audit prompt
46+
id: read-prompt
47+
run: |
48+
{
49+
echo 'PROMPT<<PROMPT_EOF'
50+
cat scripts/licenseAuditPrompt.txt
51+
echo 'PROMPT_EOF'
52+
} >> "$GITHUB_OUTPUT"
53+
4354
- name: Audit licenses with Claude
4455
uses: anthropics/claude-code-action@v1
4556
with:
4657
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
4758
claude_args: '--allowedTools "Bash,Read,Write,Glob,Grep,WebFetch"'
48-
prompt: |
49-
You are a license compliance auditor. Your job is to review the OSS dependency
50-
licenses in this repository and produce a structured audit result.
51-
52-
## Steps
53-
54-
1. Read the file `oss-licenses.json` in the repo root.
55-
56-
2. Identify every package whose `license` field is:
57-
- `"UNKNOWN"`
58-
- A non-standard SPDX string (e.g., `"SEE LICENSE IN LICENSE"`, `"UNLICENSED"`,
59-
`"SEE LICENSE IN ..."`, or any value that is not a recognized SPDX identifier)
60-
- An object instead of a string (e.g., `{"type":"MIT","url":"..."}`)
61-
62-
3. For each such package, try to resolve the actual license:
63-
- Use WebFetch to visit `https://www.npmjs.com/package/<package-name>` and look
64-
for license information on the npm page.
65-
- If the npm page is inconclusive, check the package's `repository` or `homepage`
66-
URL (from oss-licenses.json) via WebFetch to find a LICENSE file.
67-
- If the license field is an object like `{"type":"MIT","url":"..."}`, extract the
68-
`type` field as the resolved license.
69-
70-
4. Identify all copyleft-licensed packages. Classify them as:
71-
- **Strong copyleft**: GPL-2.0, GPL-3.0, AGPL-1.0, AGPL-3.0, SSPL-1.0, EUPL-1.1,
72-
EUPL-1.2, CPAL-1.0, OSL-3.0 (and any `-only` or `-or-later` variants)
73-
- **Weak copyleft**: LGPL-2.0, LGPL-2.1, LGPL-3.0, MPL-2.0, CC-BY-SA-3.0,
74-
CC-BY-SA-4.0 (and any `-only` or `-or-later` variants)
75-
76-
5. Write a file called `license-audit-result.json` in the repo root with this structure:
77-
```json
78-
{
79-
"status": "PASS or FAIL",
80-
"failReasons": ["list of reasons if FAIL, empty array if PASS"],
81-
"summary": {
82-
"totalPackages": 0,
83-
"resolvedCount": 0,
84-
"unresolvedCount": 0,
85-
"strongCopyleftCount": 0,
86-
"weakCopyleftCount": 0
87-
},
88-
"resolved": [
89-
{ "name": "pkg-name", "version": "1.0.0", "originalLicense": "...", "resolvedLicense": "MIT", "source": "npm page / GitHub repo / extracted from object" }
90-
],
91-
"unresolved": [
92-
{ "name": "pkg-name", "version": "1.0.0", "license": "UNKNOWN", "reason": "why it could not be resolved" }
93-
],
94-
"copyleft": {
95-
"strong": [
96-
{ "name": "pkg-name", "version": "1.0.0", "license": "GPL-3.0" }
97-
],
98-
"weak": [
99-
{ "name": "pkg-name", "version": "1.0.0", "license": "MPL-2.0" }
100-
]
101-
}
102-
}
103-
```
104-
105-
6. Set `status` to `"FAIL"` if `unresolvedCount > 0` OR `strongCopyleftCount > 0`.
106-
Otherwise set it to `"PASS"`.
107-
108-
7. If the status is FAIL, populate `failReasons` with human-readable explanations, e.g.:
109-
- "2 packages have unresolvable licenses: pkg-a, pkg-b"
110-
- "1 package uses strong copyleft license: pkg-c (GPL-3.0)"
111-
112-
## Important Notes
113-
- Do NOT modify any source files. Only write `license-audit-result.json`.
114-
- Be thorough: check every non-standard license, not just a sample.
115-
- If a package's license object has a `type` field, that counts as resolved.
116-
- Weak copyleft licenses (LGPL, MPL) are flagged but do NOT cause a FAIL.
59+
prompt: ${{ steps.read-prompt.outputs.PROMPT }}
11760

11861
- name: Validate audit result
11962
run: |
@@ -148,86 +91,79 @@ jobs:
14891
with:
14992
script: |
15093
const fs = require('fs');
151-
const path = 'license-audit-result.json';
94+
const resultPath = 'license-audit-result.json';
15295
153-
if (!fs.existsSync(path)) {
154-
const body = `## License Audit\n\n:x: Audit failed to produce results. Check the workflow logs for details.`;
155-
const comments = await github.rest.issues.listComments({
156-
owner: context.repo.owner,
157-
repo: context.repo.repo,
158-
issue_number: context.issue.number,
159-
});
160-
const existing = comments.data.find(c => c.body.startsWith('## License Audit'));
161-
if (existing) {
162-
await github.rest.issues.updateComment({
163-
owner: context.repo.owner,
164-
repo: context.repo.repo,
165-
comment_id: existing.id,
166-
body,
167-
});
168-
} else {
169-
await github.rest.issues.createComment({
170-
owner: context.repo.owner,
171-
repo: context.repo.repo,
172-
issue_number: context.issue.number,
173-
body,
174-
});
175-
}
176-
return;
177-
}
96+
// Determine if we should comment
97+
let shouldComment = false;
98+
let body = '';
17899
179-
const result = JSON.parse(fs.readFileSync(path, 'utf-8'));
180-
const icon = result.status === 'PASS' ? ':white_check_mark:' : ':x:';
100+
if (!fs.existsSync(resultPath)) {
101+
shouldComment = true;
102+
body = `## License Audit\n\n:x: Audit failed to produce results. Check the workflow logs for details.`;
103+
} else {
104+
const result = JSON.parse(fs.readFileSync(resultPath, 'utf-8'));
105+
const hasWeakCopyleft = result.summary.weakCopyleftCount > 0;
106+
const isFail = result.status === 'FAIL';
181107
182-
let body = `## License Audit\n\n`;
183-
body += `${icon} **Status: ${result.status}**\n\n`;
184-
body += `| Metric | Count |\n|---|---|\n`;
185-
body += `| Total packages | ${result.summary.totalPackages} |\n`;
186-
body += `| Resolved (non-standard) | ${result.summary.resolvedCount} |\n`;
187-
body += `| Unresolved | ${result.summary.unresolvedCount} |\n`;
188-
body += `| Strong copyleft | ${result.summary.strongCopyleftCount} |\n`;
189-
body += `| Weak copyleft | ${result.summary.weakCopyleftCount} |\n`;
108+
if (!isFail && !hasWeakCopyleft) {
109+
return;
110+
}
190111
191-
if (result.failReasons && result.failReasons.length > 0) {
192-
body += `\n### Fail Reasons\n\n`;
193-
for (const reason of result.failReasons) {
194-
body += `- ${reason}\n`;
112+
shouldComment = true;
113+
const icon = isFail ? ':x:' : ':warning:';
114+
115+
body = `## License Audit\n\n`;
116+
body += `${icon} **Status: ${result.status}**\n\n`;
117+
body += `| Metric | Count |\n|---|---|\n`;
118+
body += `| Total packages | ${result.summary.totalPackages} |\n`;
119+
body += `| Resolved (non-standard) | ${result.summary.resolvedCount} |\n`;
120+
body += `| Unresolved | ${result.summary.unresolvedCount} |\n`;
121+
body += `| Strong copyleft | ${result.summary.strongCopyleftCount} |\n`;
122+
body += `| Weak copyleft | ${result.summary.weakCopyleftCount} |\n`;
123+
124+
if (result.failReasons && result.failReasons.length > 0) {
125+
body += `\n### Fail Reasons\n\n`;
126+
for (const reason of result.failReasons) {
127+
body += `- ${reason}\n`;
128+
}
195129
}
196-
}
197130
198-
if (result.unresolved && result.unresolved.length > 0) {
199-
body += `\n### Unresolved Packages\n\n`;
200-
body += `| Package | Version | License | Reason |\n|---|---|---|---|\n`;
201-
for (const pkg of result.unresolved) {
202-
body += `| ${pkg.name} | ${pkg.version} | \`${pkg.license}\` | ${pkg.reason} |\n`;
131+
if (result.unresolved && result.unresolved.length > 0) {
132+
body += `\n### Unresolved Packages\n\n`;
133+
body += `| Package | Version | License | Reason |\n|---|---|---|---|\n`;
134+
for (const pkg of result.unresolved) {
135+
body += `| ${pkg.name} | ${pkg.version} | \`${pkg.license}\` | ${pkg.reason} |\n`;
136+
}
203137
}
204-
}
205138
206-
if (result.copyleft && result.copyleft.strong && result.copyleft.strong.length > 0) {
207-
body += `\n### Strong Copyleft Packages\n\n`;
208-
body += `| Package | Version | License |\n|---|---|---|\n`;
209-
for (const pkg of result.copyleft.strong) {
210-
body += `| ${pkg.name} | ${pkg.version} | \`${pkg.license}\` |\n`;
139+
if (result.copyleft && result.copyleft.strong && result.copyleft.strong.length > 0) {
140+
body += `\n### Strong Copyleft Packages\n\n`;
141+
body += `| Package | Version | License |\n|---|---|---|\n`;
142+
for (const pkg of result.copyleft.strong) {
143+
body += `| ${pkg.name} | ${pkg.version} | \`${pkg.license}\` |\n`;
144+
}
211145
}
212-
}
213146
214-
if (result.copyleft && result.copyleft.weak && result.copyleft.weak.length > 0) {
215-
body += `\n### Weak Copyleft Packages (informational)\n\n`;
216-
body += `| Package | Version | License |\n|---|---|---|\n`;
217-
for (const pkg of result.copyleft.weak) {
218-
body += `| ${pkg.name} | ${pkg.version} | \`${pkg.license}\` |\n`;
147+
if (result.copyleft && result.copyleft.weak && result.copyleft.weak.length > 0) {
148+
body += `\n### Weak Copyleft Packages (informational)\n\n`;
149+
body += `| Package | Version | License |\n|---|---|---|\n`;
150+
for (const pkg of result.copyleft.weak) {
151+
body += `| ${pkg.name} | ${pkg.version} | \`${pkg.license}\` |\n`;
152+
}
219153
}
220-
}
221154
222-
if (result.resolved && result.resolved.length > 0) {
223-
body += `\n<details><summary>Resolved Packages (${result.resolved.length})</summary>\n\n`;
224-
body += `| Package | Version | Original | Resolved | Source |\n|---|---|---|---|---|\n`;
225-
for (const pkg of result.resolved) {
226-
body += `| ${pkg.name} | ${pkg.version} | \`${pkg.originalLicense}\` | \`${pkg.resolvedLicense}\` | ${pkg.source} |\n`;
155+
if (result.resolved && result.resolved.length > 0) {
156+
body += `\n<details><summary>Resolved Packages (${result.resolved.length})</summary>\n\n`;
157+
body += `| Package | Version | Original | Resolved | Source |\n|---|---|---|---|---|\n`;
158+
for (const pkg of result.resolved) {
159+
body += `| ${pkg.name} | ${pkg.version} | \`${pkg.originalLicense}\` | \`${pkg.resolvedLicense}\` | ${pkg.source} |\n`;
160+
}
161+
body += `\n</details>\n`;
227162
}
228-
body += `\n</details>\n`;
229163
}
230164
165+
if (!shouldComment) return;
166+
231167
const comments = await github.rest.issues.listComments({
232168
owner: context.repo.owner,
233169
repo: context.repo.repo,

AGENTS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ Standard dev commands are documented in `CONTRIBUTING.md` and `package.json`. Ke
3333
- **Build deps only:** `yarn build:deps` (builds shared packages: schemas, db, shared, query-language)
3434
- **DB migrations:** `yarn dev:prisma:migrate:dev`
3535

36+
### Deprecated Packages
37+
38+
- **`packages/mcp`** - This standalone MCP package is deprecated. Do NOT modify it. MCP functionality is now handled by the web package at `packages/web/src/features/mcp/`.
39+
3640
### Non-obvious Caveats
3741

3842
- **Docker must be running** before `yarn dev`. Start it with `docker compose -f docker-compose-dev.yml up -d`. The backend will fail to connect to Redis/PostgreSQL otherwise.

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1717
- Fixed issue where ask responses would sometimes appear in the details panel while generating. [#1014](https://github.com/sourcebot-dev/sourcebot/pull/1014)
1818

1919
### Changed
20+
- Changed language detection to resolve file extensions with multiple language resolutions (e.g., .md) to the most common resolution. [#1026](https://github.com/sourcebot-dev/sourcebot/pull/1026)
2021
- Changed the `webUrl` property of the `/api/repos` api to return a URL rather than just a path. [#1014](https://github.com/sourcebot-dev/sourcebot/pull/1014)
2122

23+
## [4.15.11] - 2026-03-20
24+
25+
### Changed
26+
- Updated JumpCloud SSO documentation to clarify token endpoint authentication method requirement and AUTH_SECRET configuration. [#1022](https://github.com/sourcebot-dev/sourcebot/pull/1022)
27+
- The `ask_codebase` MCP tool is now hidden when no language model providers are configured. [#1018](https://github.com/sourcebot-dev/sourcebot/pull/1018)
28+
29+
## [4.15.10] - 2026-03-20
30+
31+
### Changed
32+
- Increased `SOURCEBOT_CHAT_MAX_STEP_COUNT` default from 20 to 100 to allow agents to perform more autonomous steps. [#1017](https://github.com/sourcebot-dev/sourcebot/pull/1017)
33+
- [EE] Fix error with Jumpcloud SSO state param [#1020](https://github.com/sourcebot-dev/sourcebot/pull/1020)
34+
2235
## [4.15.9] - 2026-03-17
2336

2437
### Added
@@ -45,10 +58,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4558
### Changed
4659
- Deprecated `EXPERIMENT_DISABLE_API_KEY_CREATION_FOR_NON_ADMIN_USERS` in favour of `DISABLE_API_KEY_CREATION_FOR_NON_OWNER_USERS`. The old variable will continue to work as a fallback. [#1007](https://github.com/sourcebot-dev/sourcebot/pull/1007)
4760

61+
### Modified
62+
- Made OSS license audit runnable locally [#1021](https://github.com/sourcebot-dev/sourcebot/pull/1021)
63+
64+
4865
## [4.15.6] - 2026-03-13
4966

5067
### Added
5168
- Added generated OpenAPI documentation for the public search, repo, and file browsing API surface. [#996](https://github.com/sourcebot-dev/sourcebot/pull/996)
69+
- Added OSS license audit github action. [#1003](https://github.com/sourcebot-dev/sourcebot/pull/1003)
5270

5371
### Fixed
5472
- [EE] Fixed account-driven permission sync silently wiping all Bitbucket Server repository permissions when the OAuth token expires on instances with anonymous access enabled. [#998](https://github.com/sourcebot-dev/sourcebot/pull/998)

CONTRIBUTING.md

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,25 @@ Want to take on an issue? Leave a comment and a maintainer may assign it to you
3636

3737
1. Install <a href="https://go.dev/doc/install"><img src="https://go.dev/favicon.ico" width="16" height="16"> go</a>, <a href="https://docs.docker.com/get-started/get-docker/"><img src="https://www.docker.com/favicon.ico" width="16" height="16"> docker</a>, and <a href="https://nodejs.org/"><img src="https://nodejs.org/favicon.ico" width="16" height="16"> NodeJS</a>. Note that a NodeJS version of at least `24` is required.
3838

39-
2. Install [ctags](https://github.com/universal-ctags/ctags) (required by zoekt)
39+
2. Install [universal-ctags](https://github.com/universal-ctags/ctags) v6.1.0 (required by zoekt). Version 6.1.0 is required — newer versions have a known incompatibility with zoekt.
40+
41+
**macOS:** Run the provided install script:
4042
```sh
41-
// macOS:
42-
brew install universal-ctags
43+
./install-ctags-macos.sh
44+
```
4345

44-
// Linux:
45-
snap install universal-ctags
46+
**Linux and other platforms:** Build from source:
47+
```sh
48+
curl https://codeload.github.com/universal-ctags/ctags/tar.gz/v6.1.0 | tar xz -C /tmp
49+
cd /tmp/ctags-6.1.0
50+
./autogen.sh
51+
./configure --program-prefix=universal- --enable-json
52+
make -j$(nproc)
53+
sudo make install
4654
```
4755

56+
After installing, set `CTAGS_COMMAND` in your `.env.development.local` to the path of the installed binary (e.g. `/usr/local/bin/universal-ctags`).
57+
4858
3. Install corepack:
4959
```sh
5060
npm install -g corepack

docs/api-reference/sourcebot-public.openapi.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"openapi": "3.0.3",
33
"info": {
44
"title": "Sourcebot Public API",
5-
"version": "v4.15.9",
5+
"version": "v4.15.11",
66
"description": "OpenAPI description for the public Sourcebot REST endpoints used for search, repository listing, and file browsing."
77
},
88
"tags": [

docs/docs/configuration/idp.mdx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,7 @@ A JumpCloud connection can be used for [authentication](/docs/configuration/auth
531531

532532
When configuring your application:
533533
- Set the SSO type to "OIDC"
534+
- Set the **Token Endpoint Authentication Method** to `client_secret_basic`. JumpCloud defaults to `client_secret_post`, but Sourcebot requires `client_secret_basic`.
534535
- Add `<sourcebot_url>/api/auth/callback/jumpcloud` to the redirect URIs (ex. https://sourcebot.coolcorp.com/api/auth/callback/jumpcloud)
535536
- Set the login URL to `<sourcebot_url>/login`
536537

@@ -539,6 +540,8 @@ A JumpCloud connection can be used for [authentication](/docs/configuration/auth
539540
<Step title="Define environment variables">
540541
The client id, secret, and issuer URL are provided to Sourcebot via environment variables. These can be named whatever you like
541542
(ex. `JUMPCLOUD_IDENTITY_PROVIDER_CLIENT_ID`, `JUMPCLOUD_IDENTITY_PROVIDER_CLIENT_SECRET`, and `JUMPCLOUD_IDENTITY_PROVIDER_ISSUER`)
543+
544+
You must also set the `AUTH_SECRET` environment variable. Generate one with `openssl rand -base64 33` and pass it to your Sourcebot deployment. While `AUTH_SECRET` is auto-generated if not provided, it must be explicitly set for SSO to work reliably across restarts.
542545
</Step>
543546
<Step title="Define the identity provider config">
544547
Create a `identityProvider` object in the [config file](/docs/configuration/config-file) with the following fields:

install-ctags-macos.sh

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/bin/sh
2+
3+
# This script installs universal-ctags v6.1.0 on macOS.
4+
# This version is pinned to match the version used in the Sourcebot Docker image.
5+
# See vendor/zoekt/install-ctags-alpine.sh for the Alpine equivalent.
6+
7+
CTAGS_VERSION=v6.1.0
8+
CTAGS_ARCHIVE_TOP_LEVEL_DIR=ctags-6.1.0
9+
INSTALL_DIR=/usr/local/bin
10+
11+
cleanup() {
12+
cd /
13+
rm -rf /tmp/ctags-$CTAGS_VERSION
14+
}
15+
16+
trap cleanup EXIT
17+
18+
set -eux
19+
20+
# Ensure required build tools are available
21+
if ! command -v brew >/dev/null 2>&1; then
22+
echo "Homebrew is required to install build dependencies. See https://brew.sh."
23+
exit 1
24+
fi
25+
26+
brew install autoconf automake pkg-config jansson
27+
28+
curl --retry 5 "https://codeload.github.com/universal-ctags/ctags/tar.gz/$CTAGS_VERSION" | tar xz -C /tmp
29+
cd /tmp/$CTAGS_ARCHIVE_TOP_LEVEL_DIR
30+
./autogen.sh
31+
./configure --program-prefix=universal- --enable-json
32+
make -j"$(sysctl -n hw.ncpu)"
33+
sudo make install
34+
35+
echo ""
36+
echo "universal-ctags installed to $INSTALL_DIR/universal-ctags"
37+
echo "Add the following to your .env.development.local:"
38+
echo " CTAGS_COMMAND=$INSTALL_DIR/universal-ctags"

packages/shared/src/env.server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ const options = {
238238
AWS_REGION: z.string().optional(),
239239

240240
SOURCEBOT_CHAT_MODEL_TEMPERATURE: numberSchema.default(0.3),
241-
SOURCEBOT_CHAT_MAX_STEP_COUNT: numberSchema.default(20),
241+
SOURCEBOT_CHAT_MAX_STEP_COUNT: numberSchema.default(100),
242242

243243
DEBUG_WRITE_CHAT_MESSAGES_TO_FILE: booleanSchema.default('false'),
244244

0 commit comments

Comments
 (0)