Skip to content

Commit 1e0734d

Browse files
authored
Merge pull request #6 from HDMowri/release/v1.0.1
v1.0.1: 18 AI models access with GitHub Copilot SDK + Test infrastructure
2 parents 74af89a + 4a67053 commit 1e0734d

21 files changed

Lines changed: 3724 additions & 470 deletions

.gitignore

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ yarn-error.log*
5454
pnpm-debug.log*
5555
lerna-debug.log*
5656

57+
# Dev scratch files
58+
test_live_registry.py
59+
analyze_coverage.py
60+
tests_backup_*/
61+
5762
# Azure
5863
.azure
5964
azure.yaml
@@ -76,3 +81,22 @@ next-steps.md
7681

7782
# Test output artifacts
7883
forensic_report.json
84+
test_report_*.txt
85+
test_report_*.json
86+
test_report_*.html
87+
e2e_result.txt
88+
push_result.txt
89+
push_fork_result.txt
90+
.pytest_cache_win/
91+
92+
# Test evidence (generated by saturation tests)
93+
test_evidence/
94+
95+
# Amplifier cache (local)
96+
.amplifier/
97+
98+
# Internal development artifacts (Windows-specific, not for public repo)
99+
investigations/
100+
mydocs
101+
run_parallel_tests.ps1
102+
test_results.txt

Makefile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ install:
3535

3636
# Run all tests (uses the 3-command pattern to avoid Windows asyncio issues)
3737
test:
38-
@echo "Running tests (step 1/3: model naming)..."
39-
$(PYTHON) -m pytest tests/test_model_naming.py -q --tb=short
38+
@echo "Running tests (step 1/3: model naming + cache)..."
39+
$(PYTHON) -m pytest tests/test_model_naming.py tests/test_model_cache.py \
40+
tests/test_model_cache_integration.py -q --tb=short
4041
@echo "Running tests (step 2/3: sync-heavy + SDK assumptions)..."
4142
$(PYTHON) -m pytest tests/test_models.py tests/test_converters.py tests/test_client.py \
4243
tests/test_exceptions.py tests/test_mount.py tests/test_mount_coverage.py \

README.md

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -76,25 +76,15 @@ amplifier provider use github-copilot --local
7676
amplifier provider use github-copilot --model claude-sonnet-4 --project
7777
```
7878

79-
### Provider Preferences in Bundles
79+
## Supported Models (18)
8080

81-
Use provider preferences for ordered model fallback:
81+
All 18 models available through your Copilot subscription are exposed at runtime:
8282

83-
```yaml
84-
provider_preferences:
85-
- provider: github-copilot
86-
model: claude-sonnet-4
87-
- provider: github-copilot
88-
model: gpt-*
89-
```
90-
91-
## Supported Models
83+
**Anthropic:** `claude-haiku-4.5`, `claude-opus-4.5`, `claude-opus-4.6`, `claude-opus-4.6-1m`, `claude-opus-4.6-fast`, `claude-sonnet-4`, `claude-sonnet-4.5`
9284

93-
All models available through your Copilot subscription are exposed at runtime. Examples:
85+
**OpenAI:** `gpt-4.1`, `gpt-5`, `gpt-5-mini`, `gpt-5.1`, `gpt-5.1-codex`, `gpt-5.1-codex-max`, `gpt-5.1-codex-mini`, `gpt-5.2`, `gpt-5.2-codex`, `gpt-5.3-codex`
9486

95-
- `claude-opus-4.5`, `claude-sonnet-4`, `claude-haiku-4.5`
96-
- `gpt-5`, `gpt-5.1`, `gpt-5.1-codex`
97-
- `gemini-3-pro-preview`
87+
**Google:** `gemini-3-pro-preview`
9888

9989
## Configuration
10090

@@ -115,7 +105,7 @@ Set `debug: true` for request/response event logging, or `debug: true, raw_debug
115105

116106
## Contract
117107

118-
| | |
108+
| Field | Value |
119109
| --- | --- |
120110
| **Module Type** | Provider |
121111
| **Module ID** | `provider-github-copilot` |
@@ -152,6 +142,20 @@ make sdk-assumptions # Before upgrading SDK
152142
make check # Full check (lint + test)
153143
```
154144

145+
### Live Integration Tests
146+
147+
Live tests require `RUN_LIVE_TESTS=1` and valid GitHub Copilot authentication:
148+
149+
```bash
150+
RUN_LIVE_TESTS=1 python -m pytest tests/integration/ -v
151+
```
152+
153+
On Windows PowerShell:
154+
155+
```powershell
156+
$env:RUN_LIVE_TESTS="1"; python -m pytest tests/integration/ -v
157+
```
158+
155159
## Dependencies
156160

157161
- `amplifier-core` (provided by Amplifier runtime, not installed separately)

amplifier_module_provider_github_copilot/__init__.py

Lines changed: 13 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -221,52 +221,26 @@ def _find_copilot_cli(config: dict[str, Any]) -> str | None:
221221
"""
222222
Find the Copilot CLI executable path.
223223
224-
Searches in order:
225-
1. config["cli_path"] (explicit configuration)
226-
2. COPILOT_CLI_PATH environment variable
227-
3. System PATH via shutil.which()
224+
Uses the SDK's bundled CLI binary by default. Falls back to system PATH
225+
if the bundled binary is not found. The SDK bundles its own CLI binary
226+
which is version-matched, avoiding potential version mismatches.
228227
229228
Args:
230-
config: Provider configuration (read-only)
229+
config: Provider configuration (unused, kept for API compatibility)
231230
232231
Returns:
233232
Resolved CLI path, or None if not found
234233
"""
235-
import os
236-
237234
try:
238-
# Get CLI path from config or environment
239-
cli_path = config.get("cli_path")
240-
241-
if not cli_path:
242-
cli_path = os.environ.get("COPILOT_CLI_PATH")
243-
244-
# If still not found, try to locate it
245-
if not cli_path:
246-
found = shutil.which("copilot") or shutil.which("copilot.exe")
247-
if found:
248-
cli_path = found
249-
250-
if not cli_path:
251-
logger.debug("[MOUNT] Copilot CLI not found in PATH or known locations")
252-
return None
253-
254-
# Verify the path exists
255-
if os.path.isabs(cli_path):
256-
if not os.path.isfile(cli_path):
257-
logger.debug(f"[MOUNT] Copilot CLI not found at: {cli_path}")
258-
return None
259-
_ensure_executable(cli_path)
260-
logger.debug(f"[MOUNT] Found Copilot CLI at absolute path: {cli_path}")
261-
return cli_path
262-
263-
found_path = shutil.which(cli_path)
264-
if found_path is None:
265-
logger.debug(f"[MOUNT] '{cli_path}' CLI not found in PATH")
266-
return None
267-
_ensure_executable(found_path)
268-
logger.debug(f"[MOUNT] Found Copilot CLI at: {found_path}")
269-
return found_path
235+
# Let SDK use its bundled CLI - only fall back to system PATH
236+
found = shutil.which("copilot") or shutil.which("copilot.exe")
237+
if found:
238+
_ensure_executable(found)
239+
logger.debug(f"[MOUNT] Found Copilot CLI at: {found}")
240+
return found
241+
242+
logger.debug("[MOUNT] Copilot CLI not found in PATH")
243+
return None
270244

271245
except Exception as e:
272246
logger.debug(f"[MOUNT] CLI path discovery failed: {e}")

amplifier_module_provider_github_copilot/_constants.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,27 @@
4949
# Valid reasoning effort levels per Copilot SDK
5050
VALID_REASONING_EFFORTS = frozenset({"low", "medium", "high", "xhigh"})
5151

52+
# ═══════════════════════════════════════════════════════════════════════════════
53+
# MODEL CACHE CONFIGURATION
54+
# ═══════════════════════════════════════════════════════════════════════════════
55+
#
56+
# The model cache persists model metadata (context_window, max_output_tokens)
57+
# to disk so that provider initialization can return accurate values without
58+
# calling the SDK API every time.
59+
#
60+
# Cache is written by list_models() (called during `amplifier init`) and read
61+
# during provider initialization (mount/constructor).
62+
#
63+
# Cross-platform: Uses pathlib.Path.home() which works on:
64+
# - Linux: /home/<user>/.amplifier/cache/
65+
# - WSL: /home/<user>/.amplifier/cache/
66+
# - macOS: /Users/<user>/.amplifier/cache/
67+
# - Windows: C:\Users\<user>\.amplifier\cache\
68+
69+
CACHE_FORMAT_VERSION = 1 # Increment on breaking schema changes
70+
CACHE_FILE_NAME = "github-copilot-models.json" # Provider-specific file
71+
CACHE_STALE_DAYS = 30 # Log warning if cache older than this
72+
5273
# Maximum repaired tool IDs to track (LRU eviction)
5374
MAX_REPAIRED_TOOL_IDS = 1000
5475

@@ -128,6 +149,20 @@
128149
# They are NOT documented but conflict with custom tools.
129150
"report_intent", # Hidden: Causes hang if custom tool uses same name
130151
"task", # Hidden: Causes hang if custom tool uses same name
152+
# ─────────────────────────────────────────────────────────────────────────
153+
# Additional built-ins discovered via archaeology (2026-02-09) and live
154+
# testing (2026-02-16). Bug: GHCP-BUILTIN-TOOLS-001
155+
# Evidence: ST04 session, binary analysis, Gemini live test
156+
# ─────────────────────────────────────────────────────────────────────────
157+
"create", # File ops: ST04 session - "Tool 'create' not found"
158+
"shell", # Shell: 2026-02-09 archaeology
159+
"report_progress", # Think: 2026-02-09 archaeology (CLI session UI)
160+
"update_todo", # Think: 2026-02-09 archaeology
161+
"skill", # Other: 2026-02-09 archaeology
162+
"fetch_copilot_cli_documentation", # Fetch: 2026-02-16 live Gemini test
163+
"search_code_subagent", # Search: 2026-02-16 binary analysis
164+
"github-mcp-server-web_search", # Search: 2026-02-16 binary analysis (MCP)
165+
"task_complete", # Task: 2026-02-17 forensic session 1541c502
131166
}
132167
)
133168

@@ -219,4 +254,17 @@ class LoopExitMethod(Enum):
219254
# These are not documented but cause session hangs if user tool has same name.
220255
"report_intent": frozenset({"report_intent"}), # Hidden: Always exclude
221256
"task": frozenset({"task"}), # Hidden: Always exclude
257+
# ─────────────────────────────────────────────────────────────────────────
258+
# Additional built-ins (Bug GHCP-BUILTIN-TOOLS-001, 2026-02-17)
259+
# ─────────────────────────────────────────────────────────────────────────
260+
"create": frozenset({"write_file"}), # Maps to write_file (same as edit)
261+
"shell": frozenset({"bash"}), # Maps to bash
262+
"update_todo": frozenset({"todo"}), # Maps to todo
263+
"skill": frozenset({"load_skill"}), # Maps to load_skill
264+
"search_code_subagent": frozenset({"grep", "glob", "delegate"}), # Composite tool
265+
"github-mcp-server-web_search": frozenset({"web_search"}), # MCP web search
266+
# Pure exclusions — no direct Amplifier equivalent
267+
"report_progress": frozenset({"todo"}), # Partial: maps to todo for task tracking
268+
"fetch_copilot_cli_documentation": frozenset(), # CLI-specific, no equivalent
269+
"task_complete": frozenset({"todo"}), # Task completion: maps to todo
222270
}

amplifier_module_provider_github_copilot/client.py

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,9 @@ def __init__(
136136
137137
Args:
138138
config: Configuration dict with optional keys:
139-
- cli_path: Path to Copilot CLI executable
140139
- log_level: Logging level for CLI
141140
- auto_restart: Whether to auto-restart CLI on crash
141+
- cwd: Working directory for CLI process
142142
timeout: Default request timeout in seconds (must be > 0)
143143
144144
Raises:
@@ -313,23 +313,11 @@ async def ensure_client(self) -> CopilotClient:
313313
def _build_client_options(self) -> dict[str, Any]:
314314
"""Build CopilotClientOptions from configuration.
315315
316-
Note: We intentionally do NOT auto-discover copilot CLI from PATH.
317316
The SDK bundles its own CLI binary which is version-matched to the SDK.
318-
Using a system-installed CLI can cause version mismatches and auth issues.
319-
Only override cli_path if explicitly configured.
317+
We always use the bundled CLI to avoid version mismatches and auth issues.
320318
"""
321319
options: dict[str, Any] = {}
322320

323-
# Only use cli_path if explicitly configured (config or env var)
324-
# Otherwise let SDK use its bundled CLI binary
325-
cli_path = self._config.get("cli_path") or os.environ.get("COPILOT_CLI_PATH")
326-
327-
if cli_path:
328-
options["cli_path"] = cli_path
329-
logger.debug(f"[CLIENT] Using explicit CLI path: {cli_path}")
330-
else:
331-
logger.debug("[CLIENT] Using SDK's bundled CLI")
332-
333321
if self._config.get("log_level"):
334322
options["log_level"] = self._config["log_level"]
335323

@@ -447,7 +435,7 @@ async def create_session(
447435
# Disable infinite sessions for ephemeral pattern
448436
session_config["infinite_sessions"] = {"enabled": False}
449437

450-
# Add tools for structured tool calling (Option A: capture-and-abort)
438+
# Add tools for structured tool calling (capture-and-abort pattern)
451439
if tools:
452440
session_config["tools"] = tools
453441
logger.debug(f"[CLIENT] Registering {len(tools)} tool(s) with session")

0 commit comments

Comments
 (0)