Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 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
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
33 changes: 33 additions & 0 deletions .claude/hooks/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`);
15 changes: 15 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"hooks": {
"SubagentStart": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "node --input-type=module -e \"import { homedir } from 'node:os'; import { join } from 'node:path'; import { readFile } from 'node:fs/promises'; const file = join(homedir(), '.claude', 'hooks', 'subagent-start-marker.js'); const source = await readFile(file, 'utf8'); const url = 'data:text/javascript;base64,' + Buffer.from(source).toString('base64'); await import(url);\""
}
]
}
]
}
}
65 changes: 65 additions & 0 deletions .opencode/plugins/subagent-marker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
const MARKER_PREFIX = "__SUBAGENT_MARKER__"

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

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 && info.parentID) {
subagentSessions.add(info.id)
}
return
}

if (event.type === "session.deleted") {
const info = getSessionInfo(event)
if (info?.id) {
subagentSessions.delete(info.id)
markedSessions.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: `${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)
},
}
}
117 changes: 106 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ 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.
Expand Down Expand Up @@ -177,6 +177,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,
"compactUseSmallModel": 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`.
- **compactUseSmallModel:** When `true`, detected "compact" requests (e.g., from Claude Code or Opencode compact mode) will automatically use the configured `smallModel` to avoid consuming premium model usage for short/background tasks. 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 +231,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 @@ -307,12 +354,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 +375,45 @@ 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 hook producer

Use the included hook script to inject marker context on `SubagentStart`.
If you place the script under your user Claude directory (`~/.claude/hooks`), use this cross-platform command in `.claude/settings.json`:

- `.claude/hooks/subagent-start-marker.js`

And enable it from `.claude/settings.json`:

```json
{
"hooks": {
"SubagentStart": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "node --input-type=module -e \"import { homedir } from 'node:os'; import { join } from 'node:path'; import { readFile } from 'node:fs/promises'; const file = join(homedir(), '.claude', 'hooks', 'subagent-start-marker.js'); const source = await readFile(file, 'utf8'); const url = 'data:text/javascript;base64,' + Buffer.from(source).toString('base64'); await import(url);\""
}
]
}
]
}
}
```

#### Opencode plugin producer

For opencode, use the plugin implementation at:

- `.opencode/plugins/subagent-marker.js`

This plugin tracks sub-sessions and prepends a marker system reminder to subagent chat messages.

## Running from Source

The project can be run from source in several ways:
Expand All @@ -349,3 +437,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.
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/**", ".opencode/**"],
prettier: {
plugins: ["prettier-plugin-packagejson"],
},
Expand Down
6 changes: 3 additions & 3 deletions src/lib/api-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ export const standardHeaders = () => ({
accept: "application/json",
})

const COPILOT_VERSION = "0.26.7"
const COPILOT_VERSION = "0.37.6"
const EDITOR_PLUGIN_VERSION = `copilot-chat/${COPILOT_VERSION}`
const USER_AGENT = `GitHubCopilotChat/${COPILOT_VERSION}`

const API_VERSION = "2025-04-01"
const API_VERSION = "2025-10-01"

export const copilotBaseUrl = (state: State) =>
state.accountType === "individual" ?
Expand All @@ -25,7 +25,7 @@ export const copilotHeaders = (state: State, vision: boolean = false) => {
"editor-version": `vscode/${state.vsCodeVersion}`,
"editor-plugin-version": EDITOR_PLUGIN_VERSION,
"user-agent": USER_AGENT,
"openai-intent": "conversation-panel",
"openai-intent": "conversation-agent",
"x-github-api-version": API_VERSION,
"x-request-id": randomUUID(),
"x-vscode-user-agent-library-version": "electron-fetch",
Expand Down
Loading