Commit f7062f2
feat: align A2A adapter with canonical response format (#83)
* docs: add CLAUDE.md with Python SDK development learnings
Document key learnings from building the Python AdCP SDK:
**Type Safety & Code Generation:**
- Auto-generate Pydantic models from upstream schemas
- Handle missing schema types with documented type aliases
- Use TYPE_CHECKING for optional dependencies
- Use cast() for JSON deserialization type safety
**Testing Strategy:**
- Mock at the right level (_get_client() not httpx class)
- httpx response.json() is SYNCHRONOUS not async
- Test the API as it exists, not as we wish it existed
**CI/CD & Release:**
- Verify secret names before changing them (PYPY_API_TOKEN not PYPI_API_TOKEN)
- Release Please automates version bumps and PyPI publishing
- Entry points in pyproject.toml enable uvx usage
**Python Patterns:**
- String escaping order matters (backslash first, then quotes)
- Atomic file operations for config files
- Connection pooling for HTTP clients
- Python 3.10+ required for | union syntax
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* feat: align A2A adapter with canonical response format
Update A2A adapter to match PR #238 specification for A2A response structure:
- Use artifacts[].parts[] structure with typed parts (DataPart, TextPart, FilePart)
- Implement "last DataPart is authoritative" rule for streaming scenarios
- Distinguish protocol-level failures (status: "failed") from task-level errors (errors array)
- Use taskId/contextId fields per A2A spec (not nested task.id)
- Add comprehensive tests for all response patterns including multiple DataParts
This ensures the Python SDK correctly handles AdCP responses over A2A protocol
per the canonical specification.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* chore: fix import ordering in test files
* fix: use first completed artifact in A2A responses
Correctly implements the A2A canonical response format for handling
multiple artifacts in streaming scenarios. When multiple artifacts exist,
now selects the first artifact with status="completed" rather than simply
using the first artifact in the array.
This handles cases where early artifacts may have status="working" and
later artifacts have status="completed", ensuring we use the correct
completed data rather than intermediate progress updates.
Updated both _extract_result() and _extract_text_part() methods to use
consistent artifact selection logic, with fallback to first artifact if
no status field is present.
Added comprehensive test case for multiple artifacts with different
statuses to verify the implementation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: extract and populate message field from A2A TextParts
Ensures that the human-readable message from A2A TextParts is properly
extracted and included in TaskResult.message for all response statuses
(completed, working, submitted, input-required), not just failures.
This provides consistent access to the natural language summary of task
results, which is valuable for logging, user feedback, and AI agent
comprehension.
Updated test to verify message extraction works correctly.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: update schema sync script for new upstream directory structure
The AdCP repository restructured schemas from `static/schemas/v1/` to
`static/schemas/source/`. Updated sync script to:
- Use correct path: `static/schemas/source` instead of `v1`
- Make index.json fetch optional (gracefully handle 404)
- Continue schema discovery even if index.json is missing
This fixes CI failures in the "Validate schemas are up-to-date" check.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* feat: add versioned AdCP schema targeting
Implements explicit AdCP version targeting for schema synchronization
and type generation. This ensures the SDK is built against a specific
AdCP specification version.
Changes:
- Added `get_adcp_version()` function to return target AdCP version
- Updated schema sync script to use versioned paths (e.g., `static/schemas/v2/`)
- Added `--version` flag to CLI to show SDK and AdCP spec versions
- Exported `get_adcp_version` in public API
Currently uses "source" as the version path since upstream hasn't
migrated to versioned directories yet. Will be updated to "v2" when
upstream completes the migration.
Usage:
`adcp --version` - Show SDK version and target AdCP spec
`python -c "from adcp import get_adcp_version; print(get_adcp_version())"`
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* feat: sync schemas from authoritative public API
Changed schema synchronization to use the canonical, versioned public API
at https://adcontextprotocol.org/schemas/ instead of GitHub repository.
Changes:
- Updated sync script to fetch from public website (authoritative source)
- Extract schema refs from index.json recursively
- Preserve directory structure (core/, enums/, etc.)
- Set target AdCP version to "v1" (current production version)
- Successfully syncs 118 schemas from v1 index
The public API provides versioned, stable schema endpoints that are
the source of truth for AdCP implementations. The website handles
version aliasing (e.g., /v1/ -> /2.4.0/) transparently.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor: move AdCP version to dedicated config file
Moved the target AdCP version from hardcoded function to a simple
ADCP_VERSION file at project root. This simplifies version management
and removes the awkward sys.path manipulation in scripts.
Changes:
- Created ADCP_VERSION file containing "v1"
- Updated get_adcp_version() to read from file
- Updated sync script to read directly from file
- Removed sys.path manipulation hack
To upgrade to v2:
1. Edit ADCP_VERSION file to "v2"
2. Run sync script to download v2 schemas
3. Regenerate types
4. Done
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor: remove version subdirectory from schema cache
Simplified schema cache structure by removing the version subdirectory.
Schemas are now stored directly in schemas/cache/ with their category
structure (core/, enums/, etc.).
Changes:
- Removed version parameter from download_schema_file()
- Store schemas directly in CACHE_DIR instead of CACHE_DIR/version/
- Removed symlink creation (no longer needed)
- Updated output messages to show target version and cache location
Structure:
Before: schemas/cache/1.0.0/core/package.json
After: schemas/cache/core/package.json
Rationale:
- SDK targets single AdCP version at build time
- No runtime version switching needed
- Simpler paths for code generation and maintenance
- When upgrading to v2, just re-sync and regenerate types
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* build: include ADCP_VERSION file in package distribution
Added ADCP_VERSION to package distribution so it's available at runtime
when the package is installed via pip.
Changes:
- Created MANIFEST.in to explicitly include ADCP_VERSION
- Updated pyproject.toml with data-files configuration
- Ensures get_adcp_version() can read the file after installation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: update schema ref fixing for new directory structure
Updated fix_schema_refs.py to work with the new schema cache structure
without version subdirectories.
Changes:
- Changed path from schemas/cache/latest to schemas/cache
- Added recursive file search (rglob) for subdirectories
- Convert absolute refs to proper relative paths based on file location
- Handle cross-directory refs (e.g., ../core/error.json)
- Handle same-directory refs (e.g., error.json)
Example conversions:
From: /schemas/2.4.0/core/error.json
In media-buy/: ../core/error.json
In core/: error.json
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: update generate_types.py to use new cache structure
The script was looking for schemas in schemas/cache/1.0.0/ which no
longer exists after we simplified the cache structure. Updated to:
- Use schemas/cache/ directly (removed version subdirectory)
- Recursively find schemas in all subdirectories (core, enums, etc.)
- Process all 115+ schema files instead of 0
This fixes the CI failure where schema generation was failing with
"Models not found in the input data".
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: remove $id fields from schemas to fix code generation
The $id fields in schemas were causing datamodel-code-generator to
fail when resolving relative $ref paths. When it saw a relative ref
like "../core/context.json" and an absolute $id like
"/schemas/2.4.0/signals/get-signals-request.json", it tried to
resolve to the filesystem path "/schemas/2.4.0/core/context.json"
which doesn't exist.
Solution: Remove $id fields from all schemas during the fix_schema_refs
step. These fields aren't needed for code generation and were causing
the FileNotFoundError.
This fixes: FileNotFoundError: [Errno 2] No such file or directory:
'/schemas/2.4.0/signals/../core/context.json'
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: download transitive schema dependencies
The sync script was only downloading schemas referenced in index.json,
missing transitive dependencies like context.json that are referenced
by other schemas but not in the index itself.
Changes:
- Added discover_transitive_refs() to extract all $ref URLs from schemas
- Follow transitive closure of dependencies during sync
- Now downloads 137 schemas instead of 118
This fixes the code generation failure where datamodel-code-generator
couldn't find context.json referenced by signal schemas.
FileNotFoundError: [Errno 2] No such file or directory:
'.schema_temp/../core/context.json'
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: rewrite relative $ref paths in flattened schemas
The generate_types.py script flattens all schemas into a single temp
directory, but relative refs like "../core/context.json" were not being
rewritten to "./context.json". This caused datamodel-code-generator to
fail when trying to resolve the relative paths.
Updated rewrite_refs() to handle:
- Absolute paths: /schemas/v1/core/error.json -> ./error.json
- Relative paths: ../core/error.json -> ./error.json
- Same-dir refs: ./error.json -> ./error.json (unchanged)
This fixes: FileNotFoundError: [Errno 2] No such file or directory:
'.schema_temp/../core/context.json'
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: handle directory path refs in schema rewriting
Some schemas (like adagents.json) have refs without ../ or ./ prefix,
just directory paths like "core/property-id.json". These weren't being
rewritten to flat references, causing the code generator to look for
subdirectories in the temp directory.
Updated rewrite_refs() to handle all path formats:
- Absolute: /schemas/v1/core/error.json -> ./error.json
- Relative: ../core/error.json -> ./error.json
- Directory: core/error.json -> ./error.json
- Same-dir: ./error.json -> ./error.json (unchanged)
The simplified logic now rewrites any ref containing "/" to just the
filename with ./ prefix.
This fixes: FileNotFoundError: [Errno 2] No such file or directory:
'.schema_temp/core/property-id.json'
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: preserve directory structure instead of flattening schemas
Critical bug: Flattening all schemas to a single directory caused
filename collisions. We have:
- media-buy/list-creative-formats-request.json
- creative/list-creative-formats-request.json
When flattened, one would overwrite the other, losing data.
Solution: Preserve the directory structure in the temp directory and
let datamodel-code-generator handle the relative refs correctly.
The schemas already have correct relative refs from fix_schema_refs.py,
so we just need to copy them as-is maintaining the directory structure.
Removed the rewrite_refs() function entirely since it's no longer needed.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: convert hyphens to underscores in directory names for Python compatibility
Python module names cannot contain hyphens, only underscores. When
datamodel-code-generator creates imports, it generates invalid syntax
like "from ..pricing-options import" which causes Black to fail parsing.
Directories with hyphens:
- pricing-options
- media-buy
- creative/asset-types
Solution: When copying schemas to temp directory, replace hyphens with
underscores in directory names (pricing-options -> pricing_options).
This makes the generated imports valid Python:
"from ..pricing_options import ..."
This fixes: black.parsing.InvalidInput: Cannot parse for target version
Python 3.10: from ..pricing-options import (...)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: rewrite schema refs to use underscores for Python module compatibility
When copying schemas to the temp directory, we now:
1. Convert directory names from hyphens to underscores (media-buy -> media_buy)
2. Rewrite $ref paths to match the renamed directories
This fixes the FileNotFoundError where datamodel-code-generator was
looking for schemas with hyphenated directory names while we had
renamed them to use underscores for valid Python identifiers.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: update consolidate_exports and post_generate_fixes for directory structure
- Updated consolidate_exports.py to recursively scan subdirectories
- Updated module path generation to handle nested directories (e.g., core.error)
- Fixed post_generate_fixes.py paths to match new directory structure
- Updated fix_forward_references() to recursively scan and fix all subdirectories
- Improved forward reference pattern matching to handle imports like "from ..core import brand_manifest as brand_manifest_1"
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: remove deprecated types from imports
Removed types that no longer exist in the generated schemas:
- Details, Domain, DomainBreakdown, Filters
- HistoryItem, MarkdownAsset, PackageStatus
- Task, TasksGetRequest, TasksGetResponse, TasksListRequest, TasksListResponse
These types were removed from the upstream AdCP specification.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: resolve test failures and align A2A protocol with spec
Systematic fixes for all 13 test failures:
1. **Pydantic Forward References** (8 tests fixed)
- Re-enabled post_generate_fixes.py to fix brand_manifest import aliases
- Added model_rebuild() calls in consolidate_exports.py for forward refs
- Fixed promoted_offerings.py to use correct import alias
2. **A2A Protocol Spec Compliance** (1 test fixed)
- Removed non-existent artifact.status field checks
- Use last artifact (most recent) per A2A spec
- Simplified logic: artifacts[-1] instead of searching for "completed"
- Updated test to match correct behavior
3. **Directory Structure Updates** (3 tests fixed)
- Updated PropertyTag/PropertyId module path expectations
- Tests now expect core/ subdirectory structure
- Made import pattern matching more flexible
4. **Schema Evolution** (1 test fixed)
- Updated Package field count from 12 to 13 fields
All 300 tests now passing. Changes align with A2A specification which
defines artifacts with parts but no status field.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* chore: sync schemas and regenerate types from upstream
Synced 6 schema updates from adcontextprotocol.org/schemas/v1:
- core/creative-asset.json - Added placement_ids field
- core/creative-filters.json - Filter updates
- core/product-filters.json - Filter updates
- media-buy/sync-creatives-request.json - Request updates
- media-buy/update-media-buy-request.json - Request updates
- protocols/adcp-extension.json - New schema for agent card extensions
Regenerated all Pydantic types to reflect schema changes.
All 300 tests passing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* feat: add get_info command to expose AdCP agent card extensions
Implement support for querying agent metadata including the new AdCP
extension fields from agent cards. This allows clients to discover:
- Agent name, description, version
- AdCP version the agent implements (extensions.adcp.adcp_version)
- Supported protocol domains (extensions.adcp.protocols_supported)
- Available tools/skills
Changes:
- Add get_agent_info() to ProtocolAdapter base class
- Implement for A2A adapter (reads from /.well-known/agent.json)
- Implement for MCP adapter (extracts from server capabilities)
- Add get_info() method to ADCPClient
- Add get_info CLI command (no parameters, returns agent metadata)
- Add comprehensive tests for both A2A and MCP adapters
Usage:
adcp myagent get_info
# Returns: name, version, protocol, tools, adcp_version, protocols_supported
All 302 tests passing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: wrap long comment line to satisfy linter
Fixes E501 line too long error in CI (103 > 100 characters).
Split comment across two lines.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: handle ADK response wrapper and interim status correctly
Fixes two issues with A2A protocol response handling:
1. **ADK Response Wrapper**: ADK wraps responses in {"response": {...}}
- Added unwrapping logic in _extract_result()
- Detects single-key dict with "response" and unwraps it
- Maintains backward compatibility with non-wrapped responses
2. **Interim Response Handling**: Working/pending/input-required states
- These interim responses don't need structured AdCP content
- Changed to return data=None for interim statuses
- Added status to metadata for tracking
- Only "completed" responses require structured AdCP payload
Tests:
- test_call_tool_with_response_wrapper: Verifies ADK wrapper unwrapping
- test_interim_response_working: Verifies interim responses work without data
- All 304 tests passing
This aligns with the A2A spec requirement that only completed responses
must contain structured AdCP content in the DataPart.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: handle ADK's inconsistent response wrapper flexibly
ADK is inconsistent - some DataParts have {"response": {...}} wrapper,
others don't. Updated unwrapping logic to always extract "response" key
when present, regardless of whether it's the only key or if there are
additional metadata keys alongside it.
Changes:
- Unwrap "response" key whenever present (not just single-key dicts)
- Handle cases where ADK includes both "response" and metadata keys
- Maintain backward compatibility with non-wrapped responses
Test added:
- test_call_tool_with_response_wrapper_and_metadata: Verifies handling
of {"response": {...}, "metadata": {...}} pattern
All 305 tests passing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor: consolidate all interim status handling
Simplified the A2A interim response handling by consolidating all
interim statuses (submitted, working, pending, input-required) into
a single code path. Previously "working" had its own special case
that was identical to the else block.
Changes:
- Removed redundant "working" special case
- Single else block handles all interim states consistently
- Clearer documentation: only "completed" requires structured AdCP data
- Added "submitted" to the list of documented interim states
Tests:
- test_interim_response_working: Verifies working status
- test_interim_response_submitted: Verifies submitted status
- Both confirm data=None for interim responses
All 306 tests passing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: update for latest schema changes and FormatId discriminated union
- Remove Render type (removed from upstream schemas)
- Add FormatId backward compatibility union (FormatId1 | FormatId2)
- Fix module-qualified FormatId references in generated code
- Add comprehensive model_rebuild() calls for FormatId-related types
- Update ImageAsset and VideoAsset to include required width/height fields
- Add PreviewCreativeRequest discriminator for request_type field
- Fix FormatId2-only references to use full union type
- Update consolidate_exports.py to include all necessary model rebuilds
Test results: 291 passing (up from 280), 15 failing (down from 26)
The remaining failures are related to upstream schema evolution that
requires updates to test fixtures and mock data.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: complete FormatId union support and update all ImageAsset usages
- Add core/**/*.py to FormatId union reference fixes
- Add GetProductsRequest and other request types to model_rebuild list
- Update all ImageAsset creations to include required width/height fields
- preview_cache.py: _create_sample_manifest_for_format_id
- test_preview_html.py: all test manifests
Test results: 306 passing (all tests passing!)
This completes the schema sync work. All tests now pass with the new
FormatId discriminated union and updated asset dimension requirements.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* feat: simplify FormatId to single type with optional dimensions
After PR #246 was merged upstream, the FormatId schema was simplified from a
complex oneOf/not/anyOf discriminated union to a single type with optional
width/height fields using JSON Schema dependencies.
This eliminates the need for:
- FormatId1 and FormatId2 union types
- fix_format_id_references() and fix_format_id_union_references() workarounds
- 25+ model_rebuild() calls for types referencing FormatId
Changes:
- Updated schemas from upstream (format-id.json now uses dependencies)
- Regenerated types with single FormatId class
- Removed FormatId union workarounds from post_generate_fixes.py
- Simplified model_rebuild() calls in consolidate_exports.py (removed all
FormatId-related rebuilds)
- Updated type imports in __init__.py and test files
All 306 tests passing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: add FormatIdParameter to __all__ exports
Fixes linter error F401 where FormatIdParameter was imported but not
included in __all__. This was missed when removing FormatId1 and FormatId2
from the exports.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* chore: update to ADCP v2 schema version
Switch from v1 to v2 to track the latest 2.x.x branch of the AdCP
specification. All schemas are currently identical between v1 and v2,
so no code changes are required.
All 306 tests passing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* chore: update schema cache hashes for v2 endpoint
Updates the cached schema index URL from v1 to v2 following the
ADCP_VERSION update. The index hash remains the same as both endpoints
currently serve identical schemas.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: preserve success state for A2A interim responses
When A2A returns interim status (submitted/working), the response has:
- success=True (request was successful)
- data=None (no structured AdCP content yet)
- status=SUBMITTED/WORKING
Previously, _parse_response() would incorrectly set success=False when
data=None, breaking the A2A protocol flow. This fix preserves the original
success state so interim responses remain successful.
For MCP, this doesn't change behavior since MCP always has data on completed
tasks, and failed tasks already have success=False.
Fixes the issue where A2A agents returning 'working' status would be treated
as failures instead of interim states.
All 306 tests passing, including A2A interim response tests.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* chore: update to AdCP 2.5.0 schema version
Updates ADCP_VERSION from v2 to 2.5.0 to track the 2.5.x patch release
line. This ensures the SDK picks up bug fixes and patches within 2.5.x
without automatically jumping to 2.6.0 or higher.
Changes:
- Updated ADCP_VERSION to 2.5.0
- Synced schemas from 2.5.0 endpoint
- Fixed schema $id references for code generation
- Regenerated types (only timestamp changed)
- Updated schema cache hashes
All 306 tests passing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent f307aae commit f7062f2
File tree
430 files changed
+4364
-12427
lines changed- schemas/cache
- 1.0.0
- core
- assets
- creative
- enums
- media-buy
- pricing-options
- signals
- core
- assets
- creative
- asset-types
- enums
- media-buy
- pricing-options
- protocols
- signals
- scripts
- src/adcp
- protocols
- types
- generated_poc
- core
- assets
- creative
- enums
- media_buy
- pricing_options
- protocols
- signals
- utils
- tests
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
430 files changed
+4364
-12427
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
63 | 63 | | |
64 | 64 | | |
65 | 65 | | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
66 | 73 | | |
67 | 74 | | |
68 | 75 | | |
| |||
Large diffs are not rendered by default.
0 commit comments