Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
8977778
feat(models): add optional Pricing field to ModelInfo
Jun 30, 2026
1a94515
refactor(models): drop Pricing.as_of and tighten schema
Jul 1, 2026
cb28489
feat(models): propagate Pricing through Rust bridges + guest crate
Jul 1, 2026
4021c6a
style: apply cargo fmt to equivalence_tests.rs
Jul 1, 2026
6b563a4
fix(session): honor config.session.metadata on session:start in the R…
microsoft-amplifier Aug 21, 2026
a55d8b9
fix(validation): reuse canonical module during path checks
microsoft-amplifier Sep 9, 2026
cbc20bc
fix(loader): isolate source-resolved module imports
microsoft-amplifier Sep 9, 2026
113e38d
feat(core): preserve rich ToolResult content
microsoft-amplifier Sep 12, 2026
5ee65ec
fix(core): enforce canonical ToolResult content boundaries
microsoft-amplifier Sep 12, 2026
f06f4e3
chore: integrate PR #103
microsoft-amplifier Sep 12, 2026
f18ebf6
merge: integrate reviewed ModelInfo pricing candidate
microsoft-amplifier Sep 12, 2026
fec3906
fix(loader): preserve canonical source module identity
microsoft-amplifier Sep 12, 2026
98429cd
chore: bump core version to 1.6.2
microsoft-amplifier Sep 12, 2026
6a8bd95
chore: synchronize Python lockfile with core 1.6.2
microsoft-amplifier Sep 12, 2026
89878d3
feat(core): preserve usage cost across transports
Sep 12, 2026
af1ab64
fix(node): await async hook handler results
dluc Sep 12, 2026
a4f5660
chore(maintenance): pin actions and refresh dependencies
danfiedler-msft Sep 12, 2026
a1054ab
test(core): stabilize hook logging and WASM hash coverage
microsoft-amplifier Sep 12, 2026
2b2bc7e
fix(core): share native hook registry with Python coordinator
microsoft-amplifier Sep 12, 2026
c15f610
fix(core): lock protobuf stub regeneration
microsoft-amplifier Sep 12, 2026
2f0018d
fix(core): preserve async hook loop ownership
microsoft-amplifier Sep 12, 2026
6763eea
test(core): keep proto compilation tests hermetic
microsoft-amplifier Sep 12, 2026
6f23ef0
feat(core): integrate canonical source-module loading
microsoft-amplifier Sep 12, 2026
e7c3f9c
test(loader): cover independent async source resolvers
microsoft-amplifier Sep 12, 2026
f21d898
fix: preserve per-injection lifetime through hook aggregation
microsoft-amplifier Sep 12, 2026
d78cb22
fix: repair generated gRPC imports and hook result compatibility
microsoft-amplifier Sep 12, 2026
667fe3c
test: cover hook injection compatibility
microsoft-amplifier Sep 12, 2026
e2cf2a6
Expose safe module load failure categories (#112)
bkrabach Sep 21, 2026
5520bb9
test: combine current main with Core rich-result carrier for review
bkrabach Sep 21, 2026
69bb861
feat(hooks): reconcile ordered injection journal with rich-result car…
bkrabach Sep 21, 2026
7dc49ef
chore: prepare Core 2.0 migration and combined-state release validation
bkrabach Sep 21, 2026
35f7aae
chore: preserve public registry lock metadata for version bump
bkrabach Sep 21, 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
11 changes: 11 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
groups:
github-actions:
patterns: ["*"]
schedule:
interval: "weekly"
cooldown:
default-days: 7
77 changes: 55 additions & 22 deletions .github/workflows/proto-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,66 +7,99 @@ on:
- 'proto/**'
- 'python/amplifier_core/_grpc_gen/**'
- 'crates/amplifier-core/src/generated/**'
- 'uv.lock'
- 'scripts/generate_grpc_stubs.py'
- '.github/workflows/proto-check.yml'
pull_request:
branches: [main]
paths:
- 'proto/**'
- 'python/amplifier_core/_grpc_gen/**'
- 'crates/amplifier-core/src/generated/**'
- 'uv.lock'
- 'scripts/generate_grpc_stubs.py'
- '.github/workflows/proto-check.yml'

jobs:
proto-sync:
name: Verify Python stubs match proto
runs-on: ubuntu-latest
env:
PROTO_CHECK_DIR: /tmp/proto_check
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0

- name: Install protoc
run: |
sudo apt-get update
sudo apt-get install -y protobuf-compiler

- name: Set up Python
uses: actions/setup-python@v5
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: '3.12'

- name: Install grpcio-tools
run: pip install grpcio-tools
- name: Install locked protobuf generator and runtimes
run: |
python - <<'PY'
import subprocess
import sys
import tomllib
from pathlib import Path

packages = tomllib.loads(Path("uv.lock").read_text())["package"]
versions = {package["name"]: package["version"] for package in packages}
names = ("grpcio", "grpcio-tools", "protobuf", "setuptools")
subprocess.check_call([
sys.executable, "-m", "pip", "install",
*(f"{name}=={versions[name]}" for name in names),
])
PY

- name: Regenerate Python stubs
run: |
mkdir -p /tmp/proto_check
mkdir -p "$PROTO_CHECK_DIR"
python -m grpc_tools.protoc \
-Iproto \
--python_out=/tmp/proto_check \
--grpc_python_out=/tmp/proto_check \
--python_out="$PROTO_CHECK_DIR" \
--grpc_python_out="$PROTO_CHECK_DIR" \
proto/amplifier_module.proto

- name: Compare with committed stubs
run: |
# Check that key message classes exist in regenerated output
python -c "
import importlib.util, sys
spec = importlib.util.spec_from_file_location('pb2', '/tmp/proto_check/amplifier_module_pb2.py')
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
required = ['ChatRequest', 'ChatResponse', 'ToolResult', 'HookResult', 'ModuleInfo']
missing = [t for t in required if not hasattr(mod, t)]
if missing:
print(f'FAIL: Missing types in regenerated stubs: {missing}')
sys.exit(1)
print('PASS: Regenerated stubs contain all required types')
"
python - <<'PY'
import os
from pathlib import Path

generated = Path(os.environ["PROTO_CHECK_DIR"])
mismatches = []
for directory in (Path("proto"), Path("python/amplifier_core/_grpc_gen")):
for filename in ("amplifier_module_pb2.py", "amplifier_module_pb2_grpc.py"):
expected = (generated / filename).read_bytes()
if directory.name == "_grpc_gen" and filename.endswith("_grpc.py"):
# The packaged service stub uses its sibling schema module.
expected = expected.replace(
b"import amplifier_module_pb2 as amplifier__module__pb2",
b"from . import amplifier_module_pb2 as amplifier__module__pb2",
)
committed = directory / filename
if committed.read_bytes() != expected:
mismatches.append(str(committed))
if mismatches:
raise SystemExit("FAIL: regenerate committed stubs: " + ", ".join(mismatches))
print("PASS: all four committed Python stubs match the locked generator")
PY

rust-build:
name: Rust build and test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable
with:
toolchain: stable

- name: Install protoc
run: |
Expand Down
29 changes: 17 additions & 12 deletions .github/workflows/rust-core-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ jobs:
name: Rust Kernel Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable
with:
toolchain: stable
components: clippy, rustfmt
- uses: Swatinem/rust-cache@v2
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- name: Run Rust tests
run: cargo test -p amplifier-core --verbose
- name: Check workspace
Expand All @@ -29,12 +30,14 @@ jobs:
name: Node.js Binding Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- uses: actions/setup-node@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable
with:
node-version: '20'
toolchain: stable
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: '24'
- name: Build native module
working-directory: bindings/node
run: |
Expand All @@ -53,10 +56,12 @@ jobs:
matrix:
python-version: ['3.11', '3.12', '3.13']
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- uses: actions/setup-python@v5
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable
with:
toolchain: stable
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: ${{ matrix.python-version }}
- name: Create venv and build
Expand Down
44 changes: 22 additions & 22 deletions .github/workflows/rust-core-wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,19 @@ jobs:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v6
- uses: Swatinem/rust-cache@v2
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
workspaces: ". -> target"
cache-on-failure: true
- uses: actions/setup-python@v6
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: '3.11'
- uses: PyO3/maturin-action@v1
- uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0
with:
args: --profile dist --out dist --interpreter python3.11
manylinux: auto
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: wheels-${{ matrix.os }}
path: dist/*.whl
Expand All @@ -36,20 +36,20 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v6
- uses: Swatinem/rust-cache@v2
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
workspaces: ". -> target"
cache-on-failure: true
- uses: actions/setup-python@v6
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: '3.11'
- uses: PyO3/maturin-action@v1
- uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0
with:
target: aarch64-unknown-linux-gnu
args: --profile dist --out dist --interpreter python3.11
manylinux: auto
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: wheels-linux-aarch64
path: dist/*.whl
Expand All @@ -59,19 +59,19 @@ jobs:
runs-on: macos-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v6
- uses: Swatinem/rust-cache@v2
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
workspaces: ". -> target"
cache-on-failure: true
- uses: actions/setup-python@v6
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: '3.11'
- uses: PyO3/maturin-action@v1
- uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0
with:
target: x86_64-apple-darwin
args: --profile dist --out dist
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: wheels-macos-x86_64
path: dist/*.whl
Expand All @@ -81,19 +81,19 @@ jobs:
runs-on: windows-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v6
- uses: Swatinem/rust-cache@v2
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
workspaces: ". -> target"
cache-on-failure: true
- uses: actions/setup-python@v6
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: '3.11'
- uses: PyO3/maturin-action@v1
- uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0
with:
target: aarch64-pc-windows-msvc
args: --profile dist --out dist
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: wheels-windows-arm64
path: dist/*.whl
Expand All @@ -107,9 +107,9 @@ jobs:
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v8
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: 'wheels-*'
merge-multiple: true
path: dist/
- uses: pypa/gh-action-pypi-publish@release/v1
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
4 changes: 2 additions & 2 deletions CONTRACTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ field name differs from the JSON key.
|-----------------|-------------|---------------|-------|
| `HookResult` | `HookResult` (BaseModel) | JSON round-trip at PyO3 boundary | Field-for-field match. Rust `HookAction` enum ↔ Python `Literal` strings. |
| `HookAction` | `Literal["continue","deny","modify","inject_context","ask_user"]` | `serde(rename_all = "snake_case")` | Enum variants map 1:1 to string literals. |
| `ToolResult` | `ToolResult` (BaseModel) | JSON round-trip | 1:1. Python adds `__str__()` and `get_serialized_output()` convenience methods. |
| `ToolResult` | `ToolResult` (BaseModel) | JSON round-trip | 1:1. `content`, when present, is a canonical non-empty ordered list of text/base64-image blocks; `None` and `[]` omit it during serialization. Rust enforces this with a private `ToolResultContent` wrapper; Python validates through PyO3. Python also adds `__str__()` and `get_serialized_output()` convenience methods. |
| `ModelInfo` | `ModelInfo` (BaseModel) | JSON round-trip | 1:1. |
| `ConfigField` | `ConfigField` (BaseModel) | JSON round-trip | Rust `default_value` ↔ Python `default`. |
| `ConfigFieldType` | `Literal["text","secret","choice","boolean"]` | snake_case | |
Expand All @@ -70,7 +70,7 @@ field name differs from the JSON key.
| `ToolSpec` | `ToolSpec` (BaseModel) | JSON round-trip | 1:1. |
| `ChatRequest` | `ChatRequest` (BaseModel) | JSON round-trip | 1:1. |
| `ToolCall` | `ToolCall` (BaseModel) | JSON round-trip | 1:1. |
| `Usage` | `Usage` (BaseModel) | JSON round-trip | 1:1. |
| `Usage` | `Usage` (BaseModel) | JSON round-trip | 1:1. `cost_usd` is optional quoted decimal text at the Rust/protobuf boundary (`Option<String>`); Python exposes it as `Decimal \| None`. None is omitted and `"0"` remains present. |
| `Degradation` | `Degradation` (BaseModel) | JSON round-trip | 1:1. |
| `ChatResponse` | `ChatResponse` (BaseModel) | JSON round-trip | 1:1. |
| `ResponseFormat` (tagged enum) | `ResponseFormat` (Union) | `serde(tag = "type")` | Text/Json/JsonSchema variants match. |
Expand Down
Loading
Loading