Skip to content
Draft
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
5 changes: 5 additions & 0 deletions .changeset/copilot-acp-provider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"helmor": patch
---

Add GitHub Copilot CLI as an agent provider via the Agent Client Protocol, with hybrid binary discovery, async image attachments, shared `.agents/skills` slash-command scanning, and accurate permission deny handling.
5 changes: 5 additions & 0 deletions .changeset/copilot-model-selection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"helmor": patch
---

Copilot model picker now shows live models fetched from the ACP server instead of a static "Default" entry.
9 changes: 9 additions & 0 deletions .changeset/copilot-models-and-modes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"helmor": patch
---

Bring GitHub Copilot to feature parity with Codex/Claude in the composer:
- Model picker is now sourced live from Copilot's ACP `SessionModelState` and applied per turn via `unstable_setSessionModel`.
- Plan mode and a Copilot-only Autopilot toggle drive ACP `setSessionMode` (interactive/plan/autopilot).
- Context-window ring + usage status are wired from Copilot's `usage_update` notifications through the same persistence path as Codex.
- Effort levels (low/medium/high/xhigh) are exposed on the static catalog entry.
3 changes: 3 additions & 0 deletions sidecar/bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions sidecar/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"typecheck": "bunx tsc --noEmit"
},
"dependencies": {
"@agentclientprotocol/sdk": "^0.21.0",
"@anthropic-ai/claude-agent-sdk": "0.2.126",
"@anthropic-ai/claude-code": "2.1.126",
"@cursor/sdk": "^1.0.12",
Expand Down
34 changes: 34 additions & 0 deletions sidecar/src/context-usage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
buildClaudeRichMeta,
buildClaudeStoredMeta,
buildCodexStoredMeta,
buildCopilotStoredMeta,
} from "./context-usage";

const CLAUDE_MODEL = "claude-opus-4-7[1m]";
Expand Down Expand Up @@ -377,3 +378,36 @@ describe("buildCodexStoredMeta", () => {
expect(meta?.modelId).toBe("");
});
});

describe("buildCopilotStoredMeta", () => {
it("maps used/size into the persisted meta shape", () => {
const meta = buildCopilotStoredMeta(
{ used: 12_000, size: 200_000 },
"claude-sonnet-4.6",
);
expect(meta).toEqual({
modelId: "claude-sonnet-4.6",
usedTokens: 12_000,
maxTokens: 200_000,
percentage: 6,
});
});

it("clamps used to size when ACP over-reports", () => {
const meta = buildCopilotStoredMeta(
{ used: 250_000, size: 200_000 },
"copilot-default",
);
expect(meta?.usedTokens).toBe(200_000);
expect(meta?.percentage).toBe(100);
});

it("falls back to a stable model id when ACP hasn't reported one", () => {
const meta = buildCopilotStoredMeta({ used: 500, size: 100_000 }, null);
expect(meta?.modelId).toBe("copilot");
});

it("returns null when both fields are zero", () => {
expect(buildCopilotStoredMeta({ used: 0, size: 0 }, null)).toBeNull();
});
});
24 changes: 24 additions & 0 deletions sidecar/src/context-usage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,27 @@ export function buildCodexStoredMeta(
percentage: computePercentage(usedClamped, max),
};
}

/**
* Build the persisted meta from a Copilot ACP `usage_update` session
* notification. ACP exposes raw `used` / `size` token counts (size =
* full context window). When the active session hasn't reported a
* model id yet (e.g. older Copilot CLI builds without
* `SessionModelState`), fall back to the constant `"copilot"` so the
* UI ring still renders.
*/
export function buildCopilotStoredMeta(
usage: { used?: number | null; size?: number | null },
modelId: string | null,
): StoredContextUsageMeta | null {
const used = num(usage.used);
const max = num(usage.size);
if (used <= 0 && max <= 0) return null;
const usedClamped = max > 0 ? Math.min(used, max) : used;
return {
modelId: modelId ?? "copilot",
usedTokens: usedClamped,
maxTokens: max,
percentage: computePercentage(usedClamped, max),
};
}
6 changes: 6 additions & 0 deletions sidecar/src/copilot-acp-session-manager.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { expect, test } from "bun:test";
import { COPILOT_ACP_ARGS } from "./copilot-acp-session-manager.js";

test("Copilot ACP uses the supported top-level --acp flag", () => {
expect(COPILOT_ACP_ARGS).toEqual(["--acp"]);
});
Loading