-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathpyproject.toml
More file actions
172 lines (159 loc) · 8.99 KB
/
Copy pathpyproject.toml
File metadata and controls
172 lines (159 loc) · 8.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
[project]
name = 'amplifier-agent'
version = '0.17.0'
requires-python = '>=3.12'
license = 'MIT'
dependencies = [
'click>=8.1',
'amplifier-foundation>=1.0.0',
'pyyaml>=6.0',
# `admin/serve_lifecycle.py` imports httpx unconditionally, and it sits on
# the import path of `__main__`, so EVERY CLI command needs it -- including
# `--version`. It used to arrive transitively via `mcp`, so this was never
# declared. mcp 2.0.0 (published 2026-07-28) switched to the separate
# `httpx2` distribution, which has a different import name; because `mcp`
# is unpinned below, every install from that date forward resolved a venv
# with `httpx2` and no `httpx`, breaking all commands with
# `ModuleNotFoundError: No module named 'httpx'`. Installs predating it
# kept working, so this presented as "works on my machine". Declare what
# we import.
'httpx>=0.27,<1',
# G4: `tool-mcp` (declared in bundle.md) Python-imports `mcp` at mount time.
# Declaring it here makes the canonical install command
# `uv tool install --from git+https://github.com/microsoft/amplifier-agent`
# work out of the box; previously hosts had to know to pass `--with mcp`,
# and forgetting it produced a downstream `Bundle' object has no attribute
# 'origins'` AttributeError that masked the real cause.
'mcp',
# amplifier_agent_http (POC) deps. These are pulled in unconditionally for
# now; once the HTTP face graduates from POC it may move to an optional
# extra (`pip install amplifier-agent[http]`). See
# amplifier-opencode-poc-plan.md.
'fastapi>=0.110',
'uvicorn>=0.27',
'sse-starlette>=2.0',
'pydantic>=2.5',
]
[project.scripts]
amplifier-agent = "amplifier_agent_cli.__main__:main"
amplifier-agent-post-install = "amplifier_agent_lib.post_install:cli_entry"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/amplifier_agent_cli", "src/amplifier_agent_lib", "src/amplifier_agent_http"]
[tool.hatch.build.targets.wheel.force-include]
"src/amplifier_agent_lib/bundle/bundle.md" = "amplifier_agent_lib/bundle/bundle.md"
"src/amplifier_agent_lib/bundle/context/system.md" = "amplifier_agent_lib/bundle/context/system.md"
"src/amplifier_agent_lib/bundle/agents/explorer.md" = "amplifier_agent_lib/bundle/agents/explorer.md"
"src/amplifier_agent_lib/bundle/agents/architect.md" = "amplifier_agent_lib/bundle/agents/architect.md"
"src/amplifier_agent_lib/bundle/agents/builder.md" = "amplifier_agent_lib/bundle/agents/builder.md"
"src/amplifier_agent_lib/bundle/agents/debugger.md" = "amplifier_agent_lib/bundle/agents/debugger.md"
"src/amplifier_agent_lib/bundle/agents/git-ops.md" = "amplifier_agent_lib/bundle/agents/git-ops.md"
"src/amplifier_agent_lib/bundle/agents/researcher.md" = "amplifier_agent_lib/bundle/agents/researcher.md"
"src/amplifier_agent_lib/protocol/spec.md" = "amplifier_agent_lib/protocol/spec.md"
# Built-in skills (user-invocable: code-review, council; the rest are council lens deps)
"src/amplifier_agent_lib/bundle/skills/code-review/SKILL.md" = "amplifier_agent_lib/bundle/skills/code-review/SKILL.md"
"src/amplifier_agent_lib/bundle/skills/council/SKILL.md" = "amplifier_agent_lib/bundle/skills/council/SKILL.md"
"src/amplifier_agent_lib/bundle/skills/intent-keeper/SKILL.md" = "amplifier_agent_lib/bundle/skills/intent-keeper/SKILL.md"
"src/amplifier_agent_lib/bundle/skills/cranky-old-sam/SKILL.md" = "amplifier_agent_lib/bundle/skills/cranky-old-sam/SKILL.md"
"src/amplifier_agent_lib/bundle/skills/crusty-old-engineer/SKILL.md" = "amplifier_agent_lib/bundle/skills/crusty-old-engineer/SKILL.md"
"src/amplifier_agent_lib/bundle/skills/restless-old-brian/SKILL.md" = "amplifier_agent_lib/bundle/skills/restless-old-brian/SKILL.md"
"src/amplifier_agent_lib/bundle/skills/user-advocate/SKILL.md" = "amplifier_agent_lib/bundle/skills/user-advocate/SKILL.md"
"src/amplifier_agent_lib/bundle/skills/tester-breaker/SKILL.md" = "amplifier_agent_lib/bundle/skills/tester-breaker/SKILL.md"
# Built-in modes (plan copied verbatim; brainstorm authored self-contained)
"src/amplifier_agent_lib/bundle/modes/plan.md" = "amplifier_agent_lib/bundle/modes/plan.md"
"src/amplifier_agent_lib/bundle/modes/brainstorm.md" = "amplifier_agent_lib/bundle/modes/brainstorm.md"
# Note: protocol/schemas and protocol/conformance/fixtures are NOT force-included.
# They are already shipped via the `packages = ["src/amplifier_agent_lib"]` entry above,
# and a second force-include of the same directories caused hatchling to abort the wheel
# build with "A second file is being added to the wheel archive at the same path".
[tool.uv.workspace]
members = ['wrappers/python-py']
[tool.uv.sources]
amplifier-foundation = { git = 'https://github.com/microsoft/amplifier-foundation', branch = 'main' }
[dependency-groups]
dev = [
'pytest>=8.4.2',
# Not used by any test we own. Required because amplifier-core registers a
# pytest plugin via entry point (amplifier_core/pytest_plugin.py) that
# unconditionally imports pytest_asyncio; without it, pytest cannot even
# collect. Do not remove without also handling that plugin.
'pytest-asyncio>=0.24.0',
'ruff>=0.6.0',
'pyright>=1.1.380',
]
# Removed with the unit test suite:
# pytest-timeout - was already unused (no --timeout, no @pytest.mark.timeout anywhere)
# jsonschema - used only by the deleted tests/test_protocol_gen.py
[tool.pyright]
pythonVersion = "3.12"
extraPaths = ["src", "wrappers/python-py/src"]
# Force pyright to resolve third-party imports from the project's local venv
# rather than the system Python install. Without this, pyright may pick a
# stale `amplifier_foundation` from a system site-packages and report bogus
# "unknown import symbol" errors for symbols (e.g., diagnose_transcript)
# that exist in the venv copy.
venvPath = "."
venv = ".venv"
[tool.ruff]
line-length = 120
target-version = 'py312'
[tool.ruff.lint]
select = ['E','F','I','B','UP','RUF','T20','TID']
ignore = ['E501']
# --- stdout discipline -------------------------------------------------------
# `amplifier-agent run` emits a machine-parseable JSON envelope on stdout, and
# BOTH wrappers parse it (wrappers/typescript/src/run-output-parser.ts,
# wrappers/python-py/src/amplifier_agent_py/run_output_parser.py). A single
# stray print() or sys.stdout write anywhere in amplifier_agent_lib corrupts
# that envelope and breaks every host integration.
#
# This replaces the former tests/test_stdout_discipline.py, which hand-rolled an
# AST scan to enforce the same invariant. T20 (flake8-print) is the standard
# rule for it; TID251 covers the sys.stdout half that T20 does not.
#
# The CLI layer is exempt: amplifier_agent_cli is *supposed* to write to stdout.
[tool.ruff.lint.flake8-tidy-imports.banned-api]
'sys.stdout'.msg = 'amplifier_agent_lib must never touch stdout directly - it corrupts the JSON envelope both wrappers parse. Write to stderr, or emit through the protocol point.'
[tool.ruff.lint.per-file-ignores]
# The CLI owns stdout. Everything else must stay off it.
'src/amplifier_agent_cli/**' = ['T20', 'TID251']
'scripts/**' = ['T20', 'TID251']
# The conformance runners and their fixtures own stdout by design: runner_py.py
# emits its JSON report there (that IS its output contract, consumed by
# verify-parity.py), and the force-exit fixture writes to stdout deliberately to
# exercise the force-exit path.
'wrappers/**' = ['T20', 'TID251']
# The e2e harness owns its stdout too: it prints progress and warnings to the
# terminal for whoever ran `make e2e`. It never writes to the wire envelope.
'tests/**' = ['T20', 'TID251']
[tool.pytest.ini_options]
# tests/ contains e2e suites and NOTHING else. That is deliberate.
#
# Two independent e2e trees live here, each gated by its own marker:
# tests/e2e/ -m dtu the contract suite, against a DTU (Linux)
# tests/windows/ -m windows an approximation, in a Windows container
# They deliberately share no framework code. tests/windows/ answers "is Windows
# support working", not "does Windows match the DTU contract"; chasing parity
# between them is a non-goal.
#
# This repo is developed spec-first: docs/spec/ defines the contract, tests/e2e/
# proves it against the real CLI and HTTP server inside a DTU, and
# .amplifier/evaluation/ measures probabilistic agent behavior. There is no unit
# test tier. Release and contract guards are standalone scripts, not tests --
# see scripts/verify-*.{sh,py} and wrappers/conformance/verify-parity.py.
#
# Consequence: `pytest tests/` NEVER means "the fast local gate". It always means
# "run the e2e suites", which is expensive. Use `make check` for the fast gate
# (`make verify` for the fuller pre-PR gate), and the harness entry points to
# drive e2e:
# uv run python tests/e2e/framework/cli.py run <suite>
# uv run tests/windows/winframework/cli.py run <suite>
testpaths = ['tests']
pythonpath = ['src']
addopts = '--import-mode=importlib -ra'
markers = [
'dtu: DTU-based end-to-end tests requiring amplifier-digital-twin',
'windows: Windows-container end-to-end tests requiring a Windows Docker engine',
]