agentshim 0.14.0__tar.gz → 0.14.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agentshim-0.14.0 → agentshim-0.14.1}/CHANGELOG.md +9 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/PKG-INFO +1 -1
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/__init__.py +1 -1
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/core/schema.py +70 -26
- {agentshim-0.14.0 → agentshim-0.14.1}/pyproject.toml +1 -1
- {agentshim-0.14.0 → agentshim-0.14.1}/.gitignore +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/README.md +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/agent.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/core/__init__.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/core/_files.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/core/env.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/core/errors.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/core/events.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/core/home.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/core/mcp.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/core/pricing.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/core/profile.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/core/provider.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/core/skills.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/core/stream.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/core/turn.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/core/usage.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/execution/__init__.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/execution/executor.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/execution/host.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/execution/transform.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/__init__.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/claude/__init__.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/claude/events.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/claude/failures.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/claude/hooks/__init__.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/claude/hooks/confine_reads.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/claude/parser.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/claude/provider.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/claude/sandbox.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/claude/scripted.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/claude/user_hooks.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/codex/__init__.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/codex/_toml.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/codex/events.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/codex/failures.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/codex/mcp_scope.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/codex/parser.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/codex/provider.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/codex/rules.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/codex/sandbox.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/codex/scripted.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/codex/skills.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/copilot/__init__.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/copilot/events.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/copilot/parser.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/copilot/provider.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/copilot/scripted.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/gemini/__init__.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/gemini/events.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/gemini/parser.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/gemini/provider.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/gemini/scripted.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/opencode/__init__.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/opencode/events.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/opencode/parser.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/opencode/provider.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/providers/opencode/scripted.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/py.typed +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/agentshim/testing/__init__.py +0 -0
- {agentshim-0.14.0 → agentshim-0.14.1}/tests/fixtures/codex/README.md +0 -0
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.14.1 (2026-10-04)
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- Schema normalization preserves descriptions, titles and examples, including
|
|
8
|
+
annotations alongside `$ref`, so structured-output field guidance reaches
|
|
9
|
+
the model. Only metadata rejected by the selected dialect is removed.
|
|
10
|
+
- The open schema dialect accepts `oneOf` variants, as Claude does.
|
|
11
|
+
|
|
3
12
|
## 0.14.0 (2026-10-04)
|
|
4
13
|
|
|
5
14
|
Codex resumed turns report invocation usage. Additive.
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Providers disagree about which JSON Schema subset their structured-output
|
|
4
4
|
flag accepts, and the failure mode is an expensive agent turn that ends in a
|
|
5
|
-
CLI parse error. These checks run before the process starts.
|
|
5
|
+
CLI parse error. These checks run before the process starts. Real CLI probes
|
|
6
|
+
on 2026-10-04 confirmed that Codex and Claude accept descriptions, titles
|
|
7
|
+
and examples; normalization preserves these annotations as model guidance.
|
|
6
8
|
"""
|
|
7
9
|
|
|
8
10
|
from __future__ import annotations
|
|
@@ -30,8 +32,6 @@ UNSUPPORTED_KEYWORDS = frozenset(
|
|
|
30
32
|
"$anchor",
|
|
31
33
|
"$dynamicAnchor",
|
|
32
34
|
"$dynamicRef",
|
|
33
|
-
"$id",
|
|
34
|
-
"$schema",
|
|
35
35
|
"allOf",
|
|
36
36
|
"contains",
|
|
37
37
|
"dependentRequired",
|
|
@@ -41,7 +41,6 @@ UNSUPPORTED_KEYWORDS = frozenset(
|
|
|
41
41
|
"maxContains",
|
|
42
42
|
"minContains",
|
|
43
43
|
"not",
|
|
44
|
-
"oneOf",
|
|
45
44
|
"patternProperties",
|
|
46
45
|
"prefixItems",
|
|
47
46
|
"propertyNames",
|
|
@@ -53,17 +52,28 @@ UNSUPPORTED_KEYWORDS = frozenset(
|
|
|
53
52
|
|
|
54
53
|
_SUBSCHEMA_MAPS = frozenset({"properties", "$defs", "definitions"})
|
|
55
54
|
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
# them. Field names are never matched against this set: ``properties`` is
|
|
60
|
-
# traversed as a map of subschemas, so a property named ``title`` survives.
|
|
55
|
+
# Metadata values are annotations or document identifiers, never subschemas.
|
|
56
|
+
# Preserve model guidance, including annotations alongside a $ref. Field
|
|
57
|
+
# names are never matched against this set: properties is a subschema map.
|
|
61
58
|
_METADATA_KEYWORDS = frozenset({"$schema", "$id", "title", "description", "examples"})
|
|
62
59
|
|
|
63
|
-
#
|
|
64
|
-
#
|
|
65
|
-
#
|
|
66
|
-
|
|
60
|
+
# Dialect policy is shared by checking and normalization. Codex's strict
|
|
61
|
+
# subset rejects document identifiers; open schemas tolerate them. Neither
|
|
62
|
+
# dialect rejects description/title/examples in the measured CLI versions.
|
|
63
|
+
_REJECTED_METADATA: dict[SchemaDialect, frozenset[str]] = {
|
|
64
|
+
SchemaDialect.STRICT: frozenset({"$schema", "$id"}),
|
|
65
|
+
SchemaDialect.OPEN: frozenset(),
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# Claude accepts oneOf variants, while Codex's strict subset rejects oneOf.
|
|
69
|
+
_DIALECT_UNSUPPORTED = {
|
|
70
|
+
SchemaDialect.STRICT: UNSUPPORTED_KEYWORDS | {"oneOf"},
|
|
71
|
+
SchemaDialect.OPEN: UNSUPPORTED_KEYWORDS,
|
|
72
|
+
}
|
|
73
|
+
_REF_ANNOTATIONS_ALLOWED = {
|
|
74
|
+
SchemaDialect.STRICT: False,
|
|
75
|
+
SchemaDialect.OPEN: True,
|
|
76
|
+
}
|
|
67
77
|
|
|
68
78
|
# Keywords whose values are data, never subschemas. ``required`` is a list of
|
|
69
79
|
# field names and ``enum``/``const`` hold instance values, so a name such as
|
|
@@ -84,7 +94,10 @@ def dialect_problems(schema: Mapping[str, Any], dialect: SchemaDialect) -> list[
|
|
|
84
94
|
``additionalProperties: false`` and lists exactly its ``properties`` in
|
|
85
95
|
``required`` (an optional value is
|
|
86
96
|
expressed as nullable instead), the root is not an ``anyOf``, and every
|
|
87
|
-
``$ref`` resolves inside the document.
|
|
97
|
+
``$ref`` resolves inside the document. Real CLI probes on 2026-10-04
|
|
98
|
+
confirmed both dialects accept ``description``, ``title`` and ``examples``
|
|
99
|
+
on schema nodes. ``STRICT`` rejects them beside ``$ref``; ``OPEN`` accepts
|
|
100
|
+
those siblings and ``oneOf`` variants.
|
|
88
101
|
"""
|
|
89
102
|
root = dict(schema)
|
|
90
103
|
walk = _Walk(root=root, dialect=dialect, problems=[])
|
|
@@ -125,9 +138,9 @@ class _Walk:
|
|
|
125
138
|
def visit(self, node: object, location: str) -> None:
|
|
126
139
|
"""Walk one schema node, appending a problem for every unsupported construct.
|
|
127
140
|
|
|
128
|
-
A ``$ref`` node terminates the walk
|
|
129
|
-
|
|
130
|
-
|
|
141
|
+
A ``$ref`` node terminates the walk after checking its metadata. The
|
|
142
|
+
target is checked where it is defined, since every local target is
|
|
143
|
+
inside the document this walk covers.
|
|
131
144
|
"""
|
|
132
145
|
if isinstance(node, list):
|
|
133
146
|
for index, value in enumerate(cast("list[object]", node)):
|
|
@@ -140,6 +153,7 @@ class _Walk:
|
|
|
140
153
|
reference = mapping.get("$ref")
|
|
141
154
|
if reference is not None:
|
|
142
155
|
self._check_ref(reference, location)
|
|
156
|
+
self._check_ref_metadata(mapping, location)
|
|
143
157
|
return
|
|
144
158
|
|
|
145
159
|
if not self._check_properties_shape(mapping, location):
|
|
@@ -157,6 +171,17 @@ class _Walk:
|
|
|
157
171
|
if self.dialect is SchemaDialect.STRICT and not _resolves(self.root, reference):
|
|
158
172
|
self.problems.append(f"{location} has a $ref {reference!r} that resolves to nothing")
|
|
159
173
|
|
|
174
|
+
def _check_ref_metadata(self, mapping: dict[str, Any], location: str) -> None:
|
|
175
|
+
"""Check metadata siblings without traversing the referenced target twice."""
|
|
176
|
+
for key in mapping:
|
|
177
|
+
if key not in _METADATA_KEYWORDS:
|
|
178
|
+
continue
|
|
179
|
+
if (
|
|
180
|
+
key in _REJECTED_METADATA[self.dialect]
|
|
181
|
+
or not _REF_ANNOTATIONS_ALLOWED[self.dialect]
|
|
182
|
+
):
|
|
183
|
+
self.problems.append(f"{location} uses unsupported keyword {key!r} beside '$ref'")
|
|
184
|
+
|
|
160
185
|
def _check_properties_shape(self, mapping: dict[str, Any], location: str) -> bool:
|
|
161
186
|
"""Check that ``properties``, if present, is an object.
|
|
162
187
|
|
|
@@ -220,9 +245,9 @@ class _Walk:
|
|
|
220
245
|
for key, value in mapping.items():
|
|
221
246
|
if key in _SUBSCHEMA_MAPS:
|
|
222
247
|
self._visit_subschema_map(value, f"{location}/{key}")
|
|
223
|
-
elif
|
|
224
|
-
|
|
225
|
-
|
|
248
|
+
elif (
|
|
249
|
+
key in _DIALECT_UNSUPPORTED[self.dialect] or key in _REJECTED_METADATA[self.dialect]
|
|
250
|
+
):
|
|
226
251
|
self.problems.append(f"{location} uses unsupported keyword {key!r}")
|
|
227
252
|
elif key in _METADATA_KEYWORDS or key in _NON_SCHEMA_KEYWORDS:
|
|
228
253
|
# Annotations or plain values: nothing below them is a schema.
|
|
@@ -269,10 +294,13 @@ def normalize(schema: Mapping[str, Any], dialect: SchemaDialect) -> dict[str, An
|
|
|
269
294
|
Generators such as Pydantic omit defaulted properties from ``required``
|
|
270
295
|
and leave ``additionalProperties`` unset, which the CLIs read as "any
|
|
271
296
|
subset of these keys, plus anything else". This closes every object,
|
|
272
|
-
requires every declared property
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
``
|
|
297
|
+
requires every declared property and drops ``default``. Descriptions,
|
|
298
|
+
titles and examples are retained at every schema node: Codex and Claude
|
|
299
|
+
accepted them in real CLI probes on 2026-10-04. For ``STRICT``, annotations
|
|
300
|
+
beside ``$ref`` move to an ``anyOf`` wrapper containing the reference;
|
|
301
|
+
``OPEN`` keeps them as siblings.
|
|
302
|
+
Only metadata rejected by the dialect is removed (``$schema`` and ``$id``
|
|
303
|
+
under ``STRICT``). Other ``$ref`` siblings are stripped as before. An open
|
|
276
304
|
map (``additionalProperties`` a schema or ``true``) is kept, since closing
|
|
277
305
|
it would change what the schema accepts; so ``dialect_problems`` on the
|
|
278
306
|
normalized schema reports exactly what the dialect cannot express.
|
|
@@ -290,10 +318,10 @@ def _normalized(node: object, dialect: SchemaDialect) -> object:
|
|
|
290
318
|
|
|
291
319
|
reference = mapping.get("$ref")
|
|
292
320
|
if isinstance(reference, str):
|
|
293
|
-
return
|
|
321
|
+
return _normalized_ref(mapping, dialect, reference)
|
|
294
322
|
|
|
295
323
|
mapping.pop("default", None)
|
|
296
|
-
for annotation in
|
|
324
|
+
for annotation in _REJECTED_METADATA[dialect]:
|
|
297
325
|
mapping.pop(annotation, None)
|
|
298
326
|
properties = mapping.get("properties")
|
|
299
327
|
if isinstance(properties, dict):
|
|
@@ -305,6 +333,8 @@ def _normalized(node: object, dialect: SchemaDialect) -> object:
|
|
|
305
333
|
mapping["required"] = []
|
|
306
334
|
|
|
307
335
|
for key, value in list(mapping.items()):
|
|
336
|
+
if key in _METADATA_KEYWORDS or key in _NON_SCHEMA_KEYWORDS:
|
|
337
|
+
continue
|
|
308
338
|
if key in _SUBSCHEMA_MAPS and isinstance(value, dict):
|
|
309
339
|
mapping[key] = {
|
|
310
340
|
name: _normalized(subschema, dialect)
|
|
@@ -315,6 +345,20 @@ def _normalized(node: object, dialect: SchemaDialect) -> object:
|
|
|
315
345
|
return mapping
|
|
316
346
|
|
|
317
347
|
|
|
348
|
+
def _normalized_ref(
|
|
349
|
+
mapping: dict[str, Any], dialect: SchemaDialect, reference: str
|
|
350
|
+
) -> dict[str, Any]:
|
|
351
|
+
"""Keep accepted annotations without introducing forbidden $ref siblings."""
|
|
352
|
+
annotations = {
|
|
353
|
+
key: value
|
|
354
|
+
for key, value in mapping.items()
|
|
355
|
+
if key in _METADATA_KEYWORDS - _REJECTED_METADATA[dialect]
|
|
356
|
+
}
|
|
357
|
+
if annotations and not _REF_ANNOTATIONS_ALLOWED[dialect]:
|
|
358
|
+
return {"anyOf": [{"$ref": reference}], **annotations}
|
|
359
|
+
return {"$ref": reference, **annotations}
|
|
360
|
+
|
|
361
|
+
|
|
318
362
|
def _close_object(mapping: dict[str, Any], dialect: SchemaDialect) -> None:
|
|
319
363
|
"""Close an object whose extra keys are merely unspecified.
|
|
320
364
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|