Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
a57c238
feature gpt-5-codex responses api
caozhiyuan Sep 26, 2025
87899a1
feat: enhance output type for function call and add content conversio…
caozhiyuan Sep 29, 2025
4fc0fa0
refactor: optimize content conversion logic in convertToolResultConte…
caozhiyuan Sep 29, 2025
2b9733b
refactor: remove unused function call output type and simplify respon…
caozhiyuan Sep 30, 2025
505f648
feat: add signature and reasoning handling to responses translation a…
caozhiyuan Sep 30, 2025
9477b45
feat: add signature to thinking messages and enhance reasoning struct…
caozhiyuan Sep 30, 2025
44551f9
refactor: remove summaryIndex from ResponsesStreamState and related h…
caozhiyuan Sep 30, 2025
708ae33
feat: enhance streaming response handling with ping mechanism
caozhiyuan Sep 30, 2025
47fb3e4
feat: responses translation add cache_read_input_tokens
caozhiyuan Oct 1, 2025
2800ed3
feat: enhance response event handling with event types and improved p…
caozhiyuan Oct 5, 2025
619d482
feat: improve event log and enhance reasoning content handling by add…
caozhiyuan Oct 7, 2025
5c6e4c6
1.fix claude code 2.0.28 warmup request consume premium request, forc…
caozhiyuan Oct 7, 2025
32cb10a
fix: the cluade code small model where max_tokens is only 512, which …
caozhiyuan Nov 3, 2025
9051a21
feat: add model reasoning efforts configuration and integrate into me…
caozhiyuan Nov 3, 2025
eeeb820
fix: ensure application directory is created when config file is missing
caozhiyuan Nov 3, 2025
3f69f13
feat: consola file logger for handler.ts
caozhiyuan Oct 29, 2025
4c0d775
fix: copolit function call returning infinite line breaks until max_t…
caozhiyuan Oct 30, 2025
1ec12db
feat: add verbose logging configuration to enhance log detail level
caozhiyuan Nov 3, 2025
174e868
fix: update verbose property to be required in State interface and ad…
caozhiyuan Nov 3, 2025
83cdfde
Merge remote-tracking branch 'remotes/origin/master' into feature/res…
caozhiyuan Nov 3, 2025
6f47926
fix: correct typo in warning message and refine whitespace handling l…
caozhiyuan Nov 6, 2025
01d4adb
fix: update token counting logic for GPT and Claude and Grok models, …
caozhiyuan Nov 10, 2025
3cdc32c
fix: extend whitespace handling in updateWhitespaceRunState to includ…
caozhiyuan Nov 19, 2025
f7835a4
Remove incompatible with copilot responses `service_tier` field (#45)
sr-tream Nov 22, 2025
318855e
feat(config): enhance model configuration with automatic defaults and…
caozhiyuan Dec 5, 2025
afb7a5c
feat(config): add useFunctionApplyPatch option and implement patch ha…
caozhiyuan Jan 10, 2026
ee5df50
fix: fix inconsistent credit consumption in chat , and adapter claude…
caozhiyuan Jan 12, 2026
f2b8476
fix: Merge tool_result and text blocks into tool_result to avoid cons…
caozhiyuan Jan 13, 2026
f3bef04
fix: improve merging of tool results and text blocks to optimize cont…
caozhiyuan Jan 16, 2026
736afa4
fix: sync stream IDs for @ai-sdk/openai compatibility with Responses API
cuipengfei Jan 17, 2026
4f22448
fix: sync stream IDs for @ai-sdk/openai compatibility with Responses…
cuipengfei Jan 19, 2026
bc205a6
fix: update mergeToolResultForClaude to handle opencode request
caozhiyuan Jan 20, 2026
6e93cfc
fix: add default thinking text for opencode compatibility in response…
caozhiyuan Jan 21, 2026
7e16a65
feat: support messages-api
caozhiyuan Jan 25, 2026
de424c1
feat: add compact model usage configuration and detection
caozhiyuan Jan 28, 2026
de08ef3
feat: filter valid thinking blocks for Claude models in Messages API
caozhiyuan Jan 28, 2026
3c12f58
feat: remove web_search tool in responses payload as it's not support…
caozhiyuan Jan 30, 2026
c2d0e6a
docs: improve configuration examples and Claude Code settings.json ex…
caozhiyuan Jan 31, 2026
f64c2c6
feat: update vscode and copilot versions, and refine anthropic-beta h…
caozhiyuan Feb 5, 2026
e2c437d
feat: opus4.6 thinking adaptive
caozhiyuan Feb 7, 2026
d0fb055
feat: enhance model capabilities with adaptive thinking support and u…
caozhiyuan Feb 7, 2026
7b3e739
fix(stream): send valid JSON in SSE ping events to prevent AI_JSONPar…
cuipengfei Feb 8, 2026
4bcbffb
feat: update vscode and copilot version
caozhiyuan Feb 11, 2026
fae8adc
feat(auth): implement API key authentication middleware and update RE…
caozhiyuan Feb 11, 2026
5c146ac
feat: enhance response translation with assistant phase handling
caozhiyuan Feb 11, 2026
0eb8e7f
feat: update fallback version for VSCode and increment Copilot version
caozhiyuan Feb 15, 2026
5b488b2
feat: implement subagent marker integration and update related handlers
caozhiyuan Feb 16, 2026
88a2e42
fix: correct search continuation logic in parseSubagentMarkerFromSyst…
caozhiyuan Feb 16, 2026
383ab1e
feat: implement claude-plugin for SubagentStart marker injection and …
caozhiyuan Feb 26, 2026
b2dbf9d
feat: enhance anthropic beta header handling in createMessages function
caozhiyuan Mar 5, 2026
47764aa
feat: enhance phase handling in responses translation
caozhiyuan Mar 5, 2026
c9686a2
feat: add context management and compaction features to responses API
caozhiyuan Mar 6, 2026
e69e6a8
feat: align with the request headers in the copilot extension version…
caozhiyuan Mar 8, 2026
8374c20
feat: enhance adaptive thinking handling in messages API to prevent e…
caozhiyuan Mar 11, 2026
ca803ba
feat: implement opencode OAuth handling
caozhiyuan Mar 12, 2026
ca2c7f9
feat: trim oauth env var and update docs
caozhiyuan Mar 12, 2026
47eb502
feat: implement findEndpointModel function to enhance claude model ma…
caozhiyuan Mar 14, 2026
75de2a3
fix: update subagent marker ID prefix for opencode 1.2.26 compatibility
caozhiyuan Mar 14, 2026
24c0867
feat: add useMessagesApi toggle to fallback to /chat/completions on h…
caozhiyuan Mar 15, 2026
090c617
feat: update getUUID function to generate standards-compliant UUIDv4 …
caozhiyuan Mar 15, 2026
97b11b2
feat: ensure that compaction message is tracked as agent initiated, c…
caozhiyuan Mar 18, 2026
dc4c62a
feat: enhance user ID parsing with JSON support and legacy fallback
caozhiyuan Mar 20, 2026
a649843
fix: update GitHub Enterprise API URL format
caozhiyuan Mar 21, 2026
ce8224c
fix: strip cache_control from payload before sending to Copilot Messa…
caozhiyuan Mar 31, 2026
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
13 changes: 13 additions & 0 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "copilot-api-marketplace",
"owner": {
"name": "copilot-api maintainers"
},
"plugins": [
{
"name": "claude-plugin",
"description": "Inject SubagentStart marker context for copilot-api initiator override",
"source": "./claude-plugin"
}
]
}
78 changes: 78 additions & 0 deletions .opencode/plugins/subagent-marker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
const MARKER_PREFIX = "__SUBAGENT_MARKER__"

const subagentSessions = new Set()
const markedSessions = new Set()
const sessionParentMap = new Map()

const getSessionInfo = (event) => {
if (!event || typeof event !== "object") return undefined
const properties = event.properties
if (!properties || typeof properties !== "object") return undefined
const info = properties.info
if (!info || typeof info !== "object") return undefined
return info
}

export const SubagentMarkerPlugin = async () => {
return {
event: async ({ event }) => {
if (event.type === "session.created") {
const info = getSessionInfo(event)
if (info?.id) {
if (info.parentID) {
subagentSessions.add(info.id)
sessionParentMap.set(info.id, info.parentID)
} else {
sessionParentMap.set(info.id, info.id)
}
}
return
}

if (event.type === "session.deleted") {
const info = getSessionInfo(event)
if (info?.id) {
subagentSessions.delete(info.id)
markedSessions.delete(info.id)
sessionParentMap.delete(info.id)
}
}
},
"chat.message": async (input, output) => {
const { sessionID } = input
if (!subagentSessions.has(sessionID) || markedSessions.has(sessionID)) {
return
}
if (!output.message?.id || !output.message?.sessionID) {
return
}

const marker = `${MARKER_PREFIX}${JSON.stringify({
session_id: sessionID,
agent_id: sessionID,
agent_type: input.agent ?? "opencode-subagent",
})}`

output.parts.unshift({
id: `prt-${output.message.id}-subagent-marker`,
sessionID: output.message.sessionID,
messageID: output.message.id,
type: "text",
text: `<system-reminder>\nSubagentStart hook additional context: ${marker}\n</system-reminder>`,
synthetic: true,
time: {
start: Date.now(),
end: Date.now(),
},
})
markedSessions.add(sessionID)
},
"chat.headers": async (input, output) => {
const { sessionID } = input
const sessionIdValue = sessionParentMap.get(sessionID)
if (sessionIdValue) {
output.headers["x-session-id"] = sessionIdValue
}
},
}
}
152 changes: 141 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,17 @@ A reverse-engineered proxy for the GitHub Copilot API that exposes it as an Open

## Features

- **OpenAI & Anthropic Compatibility**: Exposes GitHub Copilot as an OpenAI-compatible (`/v1/chat/completions`, `/v1/models`, `/v1/embeddings`) and Anthropic-compatible (`/v1/messages`) API.
- **OpenAI & Anthropic Compatibility**: Exposes GitHub Copilot as an OpenAI-compatible (`/v1/responses`, `/v1/chat/completions`, `/v1/models`, `/v1/embeddings`) and Anthropic-compatible (`/v1/messages`) API.
- **Claude Code Integration**: Easily configure and launch [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) to use Copilot as its backend with a simple command-line flag (`--claude-code`).
- **Usage Dashboard**: A web-based dashboard to monitor your Copilot API usage, view quotas, and see detailed statistics.
- **Rate Limit Control**: Manage API usage with rate-limiting options (`--rate-limit`) and a waiting mechanism (`--wait`) to prevent errors from rapid requests.
- **Manual Request Approval**: Manually approve or deny each API request for fine-grained control over usage (`--manual`).
- **Token Visibility**: Option to display GitHub and Copilot tokens during authentication and refresh for debugging (`--show-token`).
- **Flexible Authentication**: Authenticate interactively or provide a GitHub token directly, suitable for CI/CD environments.
- **Support for Different Account Types**: Works with individual, business, and enterprise GitHub Copilot plans.
- **Opencode OAuth Support**: Use opencode GitHub Copilot authentication by setting `COPILOT_API_OAUTH_APP=opencode` environment variable.
- **GitHub Enterprise Support**: Connect to GHE.com by setting `COPILOT_API_ENTERPRISE_URL` environment variable (e.g., `company.ghe.com`).
- **Custom Data Directory**: Change the default data directory (where tokens and config are stored) by setting `COPILOT_API_HOME` environment variable.

## Demo

Expand Down Expand Up @@ -177,6 +180,52 @@ The following command line options are available for the `start` command:
| ------ | ------------------------- | ------- | ----- |
| --json | Output debug info as JSON | false | none |

## Configuration (config.json)

- **Location:** `~/.local/share/copilot-api/config.json` (Linux/macOS) or `%USERPROFILE%\.local\share\copilot-api\config.json` (Windows).
- **Default shape:**
```json
{
"auth": {
"apiKeys": []
},
"extraPrompts": {
"gpt-5-mini": "<built-in exploration prompt>",
"gpt-5.1-codex-max": "<built-in exploration prompt>"
},
"smallModel": "gpt-5-mini",
"modelReasoningEfforts": {
"gpt-5-mini": "low"
},
"useFunctionApplyPatch": true,
"useMessagesApi": true
}
```
- **auth.apiKeys:** API keys used for request authentication. Supports multiple keys for rotation. Requests can authenticate with either `x-api-key: <key>` or `Authorization: Bearer <key>`. If empty or omitted, authentication is disabled.
- **extraPrompts:** Map of `model -> prompt` appended to the first system prompt when translating Anthropic-style requests to Copilot. Use this to inject guardrails or guidance per model. Missing default entries are auto-added without overwriting your custom prompts.
- **smallModel:** Fallback model used for tool-less warmup messages (e.g., Claude Code probe requests) to avoid spending premium requests; defaults to `gpt-5-mini`.
- **modelReasoningEfforts:** Per-model `reasoning.effort` sent to the Copilot Responses API. Allowed values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. If a model isn’t listed, `high` is used by default.
- **useFunctionApplyPatch:** When `true`, the server will convert any custom tool named `apply_patch` in Responses payloads into an OpenAI-style function tool (`type: "function"`) with a parameter schema so assistants can call it using function-calling semantics to edit files. Set to `false` to leave tools unchanged. Defaults to `true`.
- **useMessagesApi:** When `true`, Claude-family models that support Copilot's native `/v1/messages` endpoint will use the Messages API; otherwise they fall back to `/chat/completions`. Set to `false` to disable Messages API routing and always use `/chat/completions`. Defaults to `true`.

Edit this file to customize prompts or swap in your own fast model. Restart the server (or rerun the command) after changes so the cached config is refreshed.

## API Authentication

- **Protected routes:** All routes except `/` require authentication when `auth.apiKeys` is configured and non-empty.
- **Allowed auth headers:**
- `x-api-key: <your_key>`
- `Authorization: Bearer <your_key>`
- **CORS preflight:** `OPTIONS` requests are always allowed.
- **When no keys are configured:** Server starts normally and allows requests (authentication disabled).

Example request:

```sh
curl http://localhost:4141/v1/models \
-H "x-api-key: your_api_key"
```

## API Endpoints

The server exposes several endpoints to interact with the Copilot API. It provides OpenAI-compatible endpoints and now also includes support for Anthropic-compatible endpoints, allowing for greater flexibility with different tools and services.
Expand All @@ -185,11 +234,12 @@ The server exposes several endpoints to interact with the Copilot API. It provid

These endpoints mimic the OpenAI API structure.

| Endpoint | Method | Description |
| --------------------------- | ------ | --------------------------------------------------------- |
| `POST /v1/chat/completions` | `POST` | Creates a model response for the given chat conversation. |
| `GET /v1/models` | `GET` | Lists the currently available models. |
| `POST /v1/embeddings` | `POST` | Creates an embedding vector representing the input text. |
| Endpoint | Method | Description |
| --------------------------- | ------ | ---------------------------------------------------------------- |
| `POST /v1/responses` | `POST` | OpenAI Most advanced interface for generating model responses. |
| `POST /v1/chat/completions` | `POST` | Creates a model response for the given chat conversation. |
| `GET /v1/models` | `GET` | Lists the currently available models. |
| `POST /v1/embeddings` | `POST` | Creates an embedding vector representing the input text. |

### Anthropic Compatible Endpoints

Expand Down Expand Up @@ -255,6 +305,28 @@ npx copilot-api@latest debug --json

# Initialize proxy from environment variables (HTTP_PROXY, HTTPS_PROXY, etc.)
npx copilot-api@latest start --proxy-env

# Use opencode GitHub Copilot authentication
COPILOT_API_OAUTH_APP=opencode npx @jeffreycao/copilot-api@latest start
```

### Opencode OAuth Authentication

You can use opencode GitHub Copilot authentication instead of the default one:

```sh
# Set environment variable before running any command
export COPILOT_API_OAUTH_APP=opencode

# Then run start or auth commands
npx @jeffreycao/copilot-api@latest start
npx @jeffreycao/copilot-api@latest auth
```

Or use inline environment variable:

```sh
COPILOT_API_OAUTH_APP=opencode npx @jeffreycao/copilot-api@latest start
```

## Using the Usage Viewer
Expand Down Expand Up @@ -307,12 +379,14 @@ Here is an example `.claude/settings.json` file:
"env": {
"ANTHROPIC_BASE_URL": "http://localhost:4141",
"ANTHROPIC_AUTH_TOKEN": "dummy",
"ANTHROPIC_MODEL": "gpt-4.1",
"ANTHROPIC_DEFAULT_SONNET_MODEL": "gpt-4.1",
"ANTHROPIC_SMALL_FAST_MODEL": "gpt-4.1",
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "gpt-4.1",
"ANTHROPIC_MODEL": "gpt-5.2",
"ANTHROPIC_DEFAULT_SONNET_MODEL": "gpt-5.2",
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "gpt-5-mini",
"DISABLE_NON_ESSENTIAL_MODEL_CALLS": "1",
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1",
"BASH_MAX_TIMEOUT_MS": "600000",
"CLAUDE_CODE_ATTRIBUTION_HEADER": "0",
"CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION": "false"
},
"permissions": {
"deny": [
Expand All @@ -326,6 +400,55 @@ You can find more options here: [Claude Code settings](https://docs.anthropic.co

You can also read more about IDE integration here: [Add Claude Code to your IDE](https://docs.anthropic.com/en/docs/claude-code/ide-integrations)

### Subagent Marker Integration (Optional)

This project supports `x-initiator: agent` for subagent-originated requests.

#### Claude Code plugin producer (marketplace-based)

The marker producer is packaged as a Claude Code plugin named `claude-plugin`.

- Marketplace catalog in this repository: `.claude-plugin/marketplace.json`
- Plugin source in this repository: `claude-plugin`

Add the marketplace remotely:

```sh
/plugin marketplace add https://github.com/ericc-ch/copilot-api.git
```

Install the plugin from the marketplace:

```sh
/plugin install claude-plugin@copilot-api-marketplace
```

After installation, the plugin injects `__SUBAGENT_MARKER__...` on `SubagentStart`, and this proxy uses it to infer `x-initiator: agent`.

#### Opencode plugin producer

The marker producer is packaged as an opencode plugin located at `.opencode/plugins/subagent-marker.js`.

**Installation:**

Copy the plugin file to your opencode plugins directory:

```sh
# Clone or download this repository, then copy the plugin
cp .opencode/plugins/subagent-marker.js ~/.config/opencode/plugins/
```

Or manually create the file at `~/.config/opencode/plugins/subagent-marker.js` with the plugin content.

**Features:**

- Tracks sub-sessions created by subagents
- Automatically prepends a marker system reminder (`__SUBAGENT_MARKER__...`) to subagent chat messages
- Sets `x-session-id` header for session tracking
- Enables this proxy to infer `x-initiator: agent` for subagent-originated requests

The plugin hooks into `session.created`, `session.deleted`, `chat.message`, and `chat.headers` events to provide seamless subagent marker functionality.

## Running from Source

The project can be run from source in several ways:
Expand All @@ -349,3 +472,10 @@ bun run start
- `--rate-limit <seconds>`: Enforces a minimum time interval between requests. For example, `copilot-api start --rate-limit 30` will ensure there's at least a 30-second gap between requests.
- `--wait`: Use this with `--rate-limit`. It makes the server wait for the cooldown period to end instead of rejecting the request with an error. This is useful for clients that don't automatically retry on rate limit errors.
- If you have a GitHub business or enterprise plan account with Copilot, use the `--account-type` flag (e.g., `--account-type business`). See the [official documentation](https://docs.github.com/en/enterprise-cloud@latest/copilot/managing-copilot/managing-github-copilot-in-your-organization/managing-access-to-github-copilot-in-your-organization/managing-github-copilot-access-to-your-organizations-network#configuring-copilot-subscription-based-network-routing-for-your-enterprise-or-organization) for more details.

### CLAUDE.md Recommended Content

Please include the following in `CLAUDE.md` (for Claude usage):

- Prohibited from directly asking questions to users, MUST use AskUserQuestion tool.
- Once you can confirm that the task is complete, MUST use AskUserQuestion tool to make user confirm. The user may respond with feedback if they are not satisfied with the result, which you can use to make improvements and try again.
8 changes: 8 additions & 0 deletions claude-plugin/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "claude-plugin",
"description": "Inject SubagentStart marker context for copilot-api initiator override",
"version": "1.0.0",
"author": {
"name": "copilot-api maintainers"
}
}
15 changes: 15 additions & 0 deletions claude-plugin/hooks/hooks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"hooks": {
"SubagentStart": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/subagent-start-marker.js\""
}
]
}
]
}
}
33 changes: 33 additions & 0 deletions claude-plugin/scripts/subagent-start-marker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
async function readStdin() {
let input = "";
for await (const chunk of process.stdin) {
input += chunk;
}
return input.trim();
}

const rawInput = await readStdin();
let hookInput = {};

if (rawInput) {
try {
hookInput = JSON.parse(rawInput);
} catch {
hookInput = {};
}
}

const marker = `__SUBAGENT_MARKER__${JSON.stringify({
session_id: hookInput.session_id ?? null,
agent_id: hookInput.agent_id ?? null,
agent_type: hookInput.agent_type ?? null,
})}`;

const payload = {
hookSpecificOutput: {
hookEventName: "SubagentStart",
additionalContext: marker,
},
};

process.stdout.write(`${JSON.stringify(payload)}\n`);
1 change: 1 addition & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import config from "@echristian/eslint-config"

export default config({
ignores: ["claude-plugin/**", ".opencode/**"],
prettier: {
plugins: ["prettier-plugin-packagejson"],
},
Expand Down
Loading
Loading