Skip to content
Merged
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
76 changes: 76 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,79 @@
# Migration Guide: v2.7.x → v2.8.0

## Overview

v2.8.0 advances the pinned `github-copilot-sdk` from `1.0.7` to `1.0.15` (stable;
bundled CLI 1.0.89) and changes the packaged default model to `auto`. The SDK move is
**wire-compatible for tool forwarding**: no public API, config key, env var, or CLI
flag changes.

SDK surface changes absorbed by the provider:

- **New `Tool.is_terminal` field.** SDK 1.0.15's `copilot.tools.Tool` dataclass gains
a trailing `is_terminal: bool = False` field, which `copilot/client.py` reads by
attribute in **both** `create_session` and `resume`
(`if tool.is_terminal: definition["isTerminal"] = True`). The provider hands the
SDK a duck-typed `SDKToolWrapper`, so the wrapper now mirrors that field with a
`False` default, which omits the `isTerminal` key and keeps the tool-forwarding
request byte-identical to 1.0.7. Same shape as the v1.0.2 `defer` and v1.0.7
`metadata` additions. Without it, every tool-forwarding turn would raise
`AttributeError`. The provider never marks a tool terminal: Amplifier's
orchestrator owns tool execution.
- **Two new mode-gated session defaults pinned** (sdk-boundary MinimalMode:MUST:17-18):
`custom_agents_local_only=True` and `enable_experimental_mode=False`, mirroring
the SDK's own empty-mode defaults. Because `custom_agents_local_only` is non-None,
the SDK applies it with one extra `session.options.update` RPC after each session
is created.
- **`ReasoningEffort` Literal gains `"max"`.** The provider's fallback allowlist
already accepted `"max"`, so behavior is unchanged; `"none"` is now the only
allowlist value outside the SDK Literal.
- **40 new `SessionEventType` members**, all classified **DROP** (no kernel domain
mapping). 30 are listed explicitly in `config/data/events.yaml` (fusion,
auto-tier, workflow-run, model-call, MCP-server, sandbox, and UI telemetry, plus
`assistant.turn_retry`, `agent.interrupted`, `session.completion_receipt`); the
other 10 fall under the existing `permission.*`, `skill.*`, and `subagent.*`
wildcards.

### Also in this release: development dependency on `amplifier-core>=2.0.0`

This affects contributors, not users: the provider does not install `amplifier-core`
as a runtime dependency. The `dev` extra now requires 2.x, and `uv.lock` resolves
2.0.1 rather than 1.3.3, so routine tests use the kernel shipped by the current
Amplifier CLI. Contributors with an existing development environment should run
`uv sync --extra dev`. No user action is required.

---

## What Changed: default model is now `auto`

- **What:** The packaged default model (used when neither the request nor the
`default_model` config sets one) changes from `claude-opus-4.5` to `auto`,
Copilot's server-side router and the Copilot CLI's own default. Each turn is
dispatched to a concrete model chosen by the service. Because `auto`
advertises no limits, `get_info()` now reports `context_window=128000` and
`max_output_tokens=16384` for the default (was `200000` / `32000`), matching
the policy fallbacks the provider already applied to `auto` at runtime.
`auto` advertises no reasoning levels, so a `reasoning_effort` (caller value
or provider default) is dropped with a log once the model list is cached.
- **Action required:** none for configurations that set `default_model` or pass
a model per request. To use a fixed model, set `default_model` to an explicit
model ID that `list_models()` reports.

---

## What Changed: SDK requirement is now `github-copilot-sdk==1.0.15`

- **What:** The provider now pins `github-copilot-sdk==1.0.15` (was `==1.0.7`). The
import-time support floor is unchanged (`>=1.0.0`; pre-GA beta SDKs remain
unsupported).
- **Action required:** none. A clean `pip install` of `2.8.0` pulls `1.0.15`
automatically via the `pyproject.toml` pin; `amplifier provider install --force
github-copilot` reinstalls the pinned SDK for an existing environment.
- **Rollback:** if the previous SDK pin is required, pin
`amplifier-module-provider-github-copilot<2.8.0`.

---

# Migration Guide: v2.6.x → v2.7.0

## Overview
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ providers:
- module: provider-github-copilot
source: git+https://github.com/microsoft/amplifier-module-provider-github-copilot@main
config:
default_model: claude-opus-4.5
default_model: claude-opus-4.8
```

## Usage
Expand All @@ -127,7 +127,7 @@ amplifier provider models github-copilot

## Supported Models

Models are discovered dynamically from the SDK at runtime — the list reflects your GitHub Copilot plan. The tables below show the current public set as of SDK 1.0.7; run `amplifier provider models github-copilot` for the live list. The Context column is the default-tier prompt window; models that advertise a larger long-context tier (shown as `long=` in the live list) are reached by enabling `enable_long_context`.
Models are discovered dynamically from the SDK at runtime — the list reflects your GitHub Copilot plan. The tables below show the current public set as of SDK 1.0.15; run `amplifier provider models github-copilot` for the live list. The Context column is the default-tier prompt window; models that advertise a larger long-context tier (shown as `long=` in the live list) are reached by enabling `enable_long_context`.

**Routing:**

Expand Down Expand Up @@ -187,12 +187,12 @@ providers:
- module: provider-github-copilot
name: github-copilot
config:
default_model: claude-opus-4.5
default_model: auto
```

| Key | Default | Description |
| --- | --- | --- |
| `default_model` | `"claude-opus-4.5"` | Model used when the caller does not specify one. Any ID from `list_models()` is valid. |
| `default_model` | `"auto"` | Model used when the caller does not specify one. Any ID from `list_models()` is valid. `auto` is Copilot's server-side router (the Copilot CLI's default): each turn goes to a model the service picks. It advertises no limits or reasoning levels, so the provider reports a conservative 128k context / 16k output, and a `reasoning_effort` is dropped for it once the model list is cached. Set an explicit model ID for a fixed window, reasoning control, or reproducible routing. |
| `reasoning_effort` | `"model default"` | Reasoning effort forwarded to models that support it (e.g. `low`, `medium`, `high`). Both this default and a caller-supplied value are best-effort: forwarded when the model supports it, dropped (field omitted, server uses its own default) when it does not — so a global or inherited effort never breaks delegation to a non-reasoning model. A malformed value (typo, mixed-case) still raises `ConfigurationError`. `"model default"` or unset defers to the model. Run `amplifier provider models github-copilot` for each model's levels. |
| `enable_long_context` | `false` | Default to the model's long-context tier when the caller does not set a `context_tier`. Forwarded to the SDK as `long_context`; ignored by models without a long tier. |
| `raw` | `false` | Include raw SDK payloads as a `"raw"` field in `llm:request` / `llm:response` events. See [Raw Payload Logging](#raw-payload-logging). |
Expand Down Expand Up @@ -388,7 +388,7 @@ For module structure, design decisions, and contract index see [docs/ARCHITECTUR
metadata: the provider obtains the selected model's default-tier (or opted-in
long-tier) prompt budget from SDK model discovery. They are not a per-request
admission decision or a backend acceptance guarantee.
- GitHub Copilot SDK 1.0.7 exposes experimental `context_info` and
- GitHub Copilot SDK 1.0.15 exposes experimental `context_info` and
`recompute_context_tokens` operations only for an already-created SDK
session. They do not accept arbitrary next-request content, so this provider
does not advertise a native `provider_count` preflight measurement.
Expand Down Expand Up @@ -548,7 +548,7 @@ Running `amplifier init` before authentication:
## Dependencies

- `amplifier-core` (provided by Amplifier runtime, not installed separately)
- `github-copilot-sdk==1.0.7`
- `github-copilot-sdk==1.0.15`
- `pyyaml>=6.0`

> **Note:** `github-copilot-sdk` is installed automatically when you install or initialize
Expand Down
10 changes: 5 additions & 5 deletions amplifier_module_provider_github_copilot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def _check_sdk_version(version_str: str) -> None:
the technical symbol-availability minimum sits *below* this floor. The floor
is deliberately set higher, at GA, to drop beta-SDK support entirely.

The exact pyproject pin (``==1.0.7``) is enforced separately by
The exact pyproject pin (``==1.0.15``) is enforced separately by
``tests/_sdk_version_gate.py``; this guard only enforces the GA floor.

Extracted for testability — module-level code that runs under
Expand All @@ -75,8 +75,8 @@ def _check_sdk_version(version_str: str) -> None:
raise ImportError(
f"github-copilot-sdk=={version_str} is below the minimum supported "
"version (>=1.0.0 required; pre-GA beta SDKs are no longer supported). "
"Pinned target: ==1.0.7 (pyproject.toml). "
"Reinstall with: pip install 'github-copilot-sdk==1.0.7' "
"Pinned target: ==1.0.15 (pyproject.toml). "
"Reinstall with: pip install 'github-copilot-sdk==1.0.15' "
f"or: amplifier provider install --force {PROVIDER_ID}"
)

Expand Down Expand Up @@ -105,7 +105,7 @@ def _parse_sdk_version(version_str: str) -> _Version:
# SDK required; tests only run with SDK installed
raise ImportError(
"Required dependency 'github-copilot-sdk' is not installed. "
"Install with: pip install 'github-copilot-sdk==1.0.7'"
"Install with: pip install 'github-copilot-sdk==1.0.15'"
) from _e
# Contract: sdk-boundary:Membrane:MUST:5 — fail at import time on wrong version.
_check_sdk_version(_sdk_version)
Expand All @@ -127,7 +127,7 @@ def _parse_sdk_version(version_str: str) -> _Version:

# Contract: provider-protocol:public_api:MUST:1 — must match pyproject.toml [project].version
# Verified by tests/test_behaviors.py::TestPackageVersionConsistency
__version__ = "2.7.0"
__version__ = "2.8.0"

# Amplifier module metadata
__amplifier_module_type__ = "provider"
Expand Down
16 changes: 10 additions & 6 deletions amplifier_module_provider_github_copilot/config/_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,19 @@
# NOTE: Per-model capabilities (vision, thinking) are set dynamically in models.py
# based on SDK's supports_vision/supports_reasoning_effort flags
"capabilities": ["streaming", "tools"],
# Updated defaults to use claude-opus-4.5 with SDK-verified limits
# SDK limits: max_context_window=200000, max_prompt_tokens=168000
# max_output_tokens = context_window - max_prompt_tokens = 32000
# Default model "auto" is Copilot's server-side router (the Copilot CLI's own
# default): each turn is dispatched to a concrete model chosen by the
# service. list_models() reports "auto" with empty limits and no reasoning
# efforts, so its window is unknown until routing happens. context_window /
# max_output_tokens therefore mirror FALLBACKS below, which is also exactly
# what model_translation derives for "auto" at runtime; get_info() reports
# the same values on a cold and a warm model cache.
"defaults": {
"model": "claude-opus-4.5",
"model": "auto",
"max_tokens": 4096,
"timeout": 3600,
"context_window": 200000,
"max_output_tokens": 32000,
"context_window": 128000,
"max_output_tokens": 16384,
},
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
class MinimalModeConfig:
"""Minimal mode session configuration policy.

Contract: sdk-boundary:MinimalMode:MUST:1-16
Contract: sdk-boundary:MinimalMode:MUST:1-18

Disables SDK features that Amplifier handles, ensuring Amplifier is the sole
orchestrator. Evidence: 57% wall-clock improvement (12.5s → 5.4s) confirmed
Expand Down Expand Up @@ -113,6 +113,26 @@ class MinimalModeConfig:
# sdk-boundary:MinimalMode SDK-introspection closure).
memory: dict[str, Any] = field(default_factory=lambda: {"enabled": False})

# MUST:17: Restrict custom-agent loading to local definitions only; Amplifier
# orchestrates agents (complements MUST:5 `custom_agents=[]`). Mode-gated
# create_session kwarg with empty-mode default helper
# `_custom_agents_local_only_default` (v1.0.15 `_mode.py:254-259`) that
# returns True ONLY when `mode == "empty"`. Under `mode="copilot-cli"` the
# helper returns None and the bundled-CLI default would otherwise apply.
# The SDK emits the value in the create payload and, because it is non-None,
# also applies it via one post-create `session.options.update` RPC
# (`_post_create_options_patch`, v1.0.15 `_mode.py:295-340`). Value mirrors
# the SDK empty-mode default.
custom_agents_local_only: bool = True

# MUST:18: Disable SDK experimental features; Amplifier needs a stable,
# reproducible runtime surface. Mode-gated create_session kwarg with
# empty-mode default helper `_enable_experimental_mode_default` (v1.0.15
# `_mode.py:262-267`) that returns False ONLY when `mode == "empty"`; under
# `mode="copilot-cli"` the runtime decides when omitted. Value mirrors the
# SDK empty-mode default.
enable_experimental_mode: bool = False


@dataclass(frozen=True)
class ToolCaptureConfig:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,12 @@ error_mappings:
field: conflict_type

# P4: Model/provider configuration errors
# "is not supported for model": SDK v1.0.15 (bundled CLI 1.0.89) rejects an
# unsupported reasoning_effort at send time via a session.error whose message
# is "Reasoning effort '<value>' is not supported for model '<model>'."
# (earlier SDKs rejected at session.create with "does not support ...").
- sdk_patterns: ["ConfigurationError"]
string_patterns: ["does not support", "configuration error", "not configured"]
string_patterns: ["does not support", "is not supported for model", "configuration error", "not configured"]
kernel_error: ConfigurationError
retryable: false
# Context extraction for better debugging
Expand Down
83 changes: 83 additions & 0 deletions amplifier_module_provider_github_copilot/config/data/events.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,89 @@ event_classifications:
- mcp.tools.list_changed
- mcp.resources.list_changed
- mcp.prompts.list_changed
# SDK v1.0.15 (bundled CLI 1.0.89): 40 new SessionEventType enum members
# since v1.0.7. All DROP: the provider's domain surface is single-completion
# + tool-call + usage + error + content-deltas; none of these carry a kernel
# domain mapping. Ten are already covered by the existing wildcards above:
# permission.assentDetected / .carriedForward / .contextualAuthorization /
# .messageAuthorization / .messageAuthorizationDegraded /
# .messageAuthorizationRead (permission.*), skill.context_delivered /
# .context_delivered_ref / .invoked_ref (skill.*), subagent.configured
# (subagent.*). The remaining 30 are listed explicitly:
# assistant.turn_retry: extra model inference attempt inside an existing
# turn (turn_id/model/reason). The retried output still arrives via the
# bridged message/usage events; bridging this marker would add nothing.
# agent.interrupted: user-interrupt telemetry (activity/elapsed/tool
# names). The provider aborts sessions itself after tool capture; turn
# completion and failures travel via session.idle / session.error.
# model.call_start / model.call_finished: per-dispatch model lifecycle
# telemetry; siblings of model.call_failure (DROP above). Usage rides
# assistant.usage -> USAGE_UPDATE.
# prompt_cache_break: prompt-cache loss diagnostics; no domain value.
# session.completion_receipt: behavior-neutral record of runtime facts at
# completion; TURN_COMPLETE already comes from assistant.message /
# session.idle, so bridging would double-emit.
# session.context_cleared: host-initiated history clear; the provider never
# clears SDK history (ephemeral per-complete() sessions).
# assistant.fusion_phase_started / _activity / _completed / _failed,
# session.fusion_route_started / _route_failed / _resolved / _completed:
# EXPERIMENTAL HydraFusion multi-model routing telemetry; the provider
# selects models explicitly.
# session.auto_tier_recommendation / session.auto_tier_switch_failed:
# Auto-tier preference signals; siblings of auto_mode_switch.* and
# session.auto_mode_resolved (DROP above).
# session.model_deselected: explicit model withdrawn by its publishing host;
# the provider passes the model on every create_session.
# session.mode_notice_delivered: mode-transition notice bookkeeping for
# resume; sibling of session.mode_changed (DROP above).
# session.permission_recovery: Autopilot permission-recovery snapshot;
# sibling of session.permissions_changed (DROP above). The provider
# denies every permission request (deny-destroy).
# sandbox.decision: process-sandbox governance record; the SDK executes no
# tools under deny-destroy, so there is nothing to surface.
# session.indexed_search: transient indexed-search diagnostics; Amplifier
# owns retrieval (skip_embedding_retrieval=True).
# session.managed_settings_enforced: enterprise-policy enforcement notice;
# sibling of session.managed_settings_resolved (DROP above).
# session.mcp_server_removed / session.mcp_server_needs_reconnect: MCP
# server lifecycle; siblings of session.mcp_server_status_changed (DROP
# above). mcp_servers={} (MinimalMode:MUST:3).
# tool_search.activated: lazy tool-search activations restored on resume;
# the provider sets defer=None on every tool and never resumes sessions.
# ui.ephemeral_query: transient side-query output that does not modify
# conversation history; no UI host.
# workflow.run_started / _updated / _settled: ephemeral workflow-run
# lifecycle signals; Amplifier orchestrates its own workflows.
- assistant.turn_retry
- agent.interrupted
- model.call_start
- model.call_finished
- prompt_cache_break
- session.completion_receipt
- session.context_cleared
- assistant.fusion_phase_started
- assistant.fusion_phase_activity
- assistant.fusion_phase_completed
- assistant.fusion_phase_failed
- session.fusion_route_started
- session.fusion_route_failed
- session.fusion_resolved
- session.fusion_completed
- session.auto_tier_recommendation
- session.auto_tier_switch_failed
- session.model_deselected
- session.mode_notice_delivered
- session.permission_recovery
- sandbox.decision
- session.indexed_search
- session.managed_settings_enforced
- session.mcp_server_removed
- session.mcp_server_needs_reconnect
- tool_search.activated
- ui.ephemeral_query
- workflow.run_started
- workflow.run_updated
- workflow.run_settled

finish_reason_map:
# Values MUST be lowercase to match amplifier-core proto:
Expand Down
Loading
Loading