python-hwpx-automation 6.0.3__py3-none-any.whl

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.
Files changed (217) hide show
  1. hwpx_automation/__init__.py +61 -0
  2. hwpx_automation/__init__.pyi +27 -0
  3. hwpx_automation/__main__.py +8 -0
  4. hwpx_automation/agent_document.py +392 -0
  5. hwpx_automation/api.py +136 -0
  6. hwpx_automation/blind_eval.py +407 -0
  7. hwpx_automation/capabilities.py +110 -0
  8. hwpx_automation/compat.py +48 -0
  9. hwpx_automation/configuration.py +60 -0
  10. hwpx_automation/core/__init__.py +2 -0
  11. hwpx_automation/core/content.py +762 -0
  12. hwpx_automation/core/context.py +111 -0
  13. hwpx_automation/core/diff.py +53 -0
  14. hwpx_automation/core/document.py +37 -0
  15. hwpx_automation/core/formatting.py +513 -0
  16. hwpx_automation/core/handles.py +24 -0
  17. hwpx_automation/core/locations.py +205 -0
  18. hwpx_automation/core/locator.py +162 -0
  19. hwpx_automation/core/plan.py +680 -0
  20. hwpx_automation/core/resources.py +42 -0
  21. hwpx_automation/core/search.py +296 -0
  22. hwpx_automation/core/transactions.py +434 -0
  23. hwpx_automation/core/txn.py +48 -0
  24. hwpx_automation/document_state.py +95 -0
  25. hwpx_automation/errors.py +174 -0
  26. hwpx_automation/execution_lock.py +15 -0
  27. hwpx_automation/fastmcp_adapter.py +672 -0
  28. hwpx_automation/form_fill.py +1177 -0
  29. hwpx_automation/form_output_models.py +223 -0
  30. hwpx_automation/handlers/__init__.py +2 -0
  31. hwpx_automation/handlers/_shared.py +377 -0
  32. hwpx_automation/handlers/agent_document.py +257 -0
  33. hwpx_automation/handlers/authoring.py +750 -0
  34. hwpx_automation/handlers/content_edit.py +1078 -0
  35. hwpx_automation/handlers/form_fill.py +607 -0
  36. hwpx_automation/handlers/layout_style.py +660 -0
  37. hwpx_automation/handlers/quality_render.py +566 -0
  38. hwpx_automation/handlers/read_export.py +1295 -0
  39. hwpx_automation/handlers/specialized.py +624 -0
  40. hwpx_automation/handlers/tracked_changes.py +589 -0
  41. hwpx_automation/handlers/workflow.py +105 -0
  42. hwpx_automation/hwp_converter.py +227 -0
  43. hwpx_automation/hwp_support.py +94 -0
  44. hwpx_automation/hwpx_ops.py +1439 -0
  45. hwpx_automation/identity.json +263 -0
  46. hwpx_automation/identity.py +18 -0
  47. hwpx_automation/ingest_adapters.py +85 -0
  48. hwpx_automation/markdown_plan.py +216 -0
  49. hwpx_automation/mcp_cli.py +29 -0
  50. hwpx_automation/metadata/tools_meta.py +40 -0
  51. hwpx_automation/mixed_form.py +3007 -0
  52. hwpx_automation/mutation_models.py +401 -0
  53. hwpx_automation/network_policy.py +232 -0
  54. hwpx_automation/office/__init__.py +14 -0
  55. hwpx_automation/office/agent/__init__.py +125 -0
  56. hwpx_automation/office/agent/_batch_verification.py +383 -0
  57. hwpx_automation/office/agent/blueprint/__init__.py +58 -0
  58. hwpx_automation/office/agent/blueprint/bundle.py +282 -0
  59. hwpx_automation/office/agent/blueprint/catalog.py +136 -0
  60. hwpx_automation/office/agent/blueprint/dump.py +520 -0
  61. hwpx_automation/office/agent/blueprint/mapping.py +312 -0
  62. hwpx_automation/office/agent/blueprint/model.py +722 -0
  63. hwpx_automation/office/agent/blueprint/native.py +621 -0
  64. hwpx_automation/office/agent/blueprint/replay.py +622 -0
  65. hwpx_automation/office/agent/catalog.py +252 -0
  66. hwpx_automation/office/agent/cli.py +647 -0
  67. hwpx_automation/office/agent/commands.py +1383 -0
  68. hwpx_automation/office/agent/document.py +801 -0
  69. hwpx_automation/office/agent/form_plan.py +1760 -0
  70. hwpx_automation/office/agent/model.py +808 -0
  71. hwpx_automation/office/agent/path.py +155 -0
  72. hwpx_automation/office/agent/query.py +230 -0
  73. hwpx_automation/office/agent/story.py +207 -0
  74. hwpx_automation/office/authoring/__init__.py +3542 -0
  75. hwpx_automation/office/authoring/advanced_generators.py +154 -0
  76. hwpx_automation/office/authoring/builder/__init__.py +52 -0
  77. hwpx_automation/office/authoring/builder/core.py +996 -0
  78. hwpx_automation/office/authoring/builder/report.py +195 -0
  79. hwpx_automation/office/authoring/design/__init__.py +30 -0
  80. hwpx_automation/office/authoring/design/_support.py +144 -0
  81. hwpx_automation/office/authoring/design/composer.py +282 -0
  82. hwpx_automation/office/authoring/design/harvest.py +305 -0
  83. hwpx_automation/office/authoring/design/plan.py +69 -0
  84. hwpx_automation/office/authoring/design/profile.py +88 -0
  85. hwpx_automation/office/authoring/design/profiles/application_form/fragments/body.xml +1 -0
  86. hwpx_automation/office/authoring/design/profiles/application_form/fragments/heading.xml +1 -0
  87. hwpx_automation/office/authoring/design/profiles/application_form/fragments/info_table.xml +1 -0
  88. hwpx_automation/office/authoring/design/profiles/application_form/fragments/title.xml +1 -0
  89. hwpx_automation/office/authoring/design/profiles/application_form/profile.json +25 -0
  90. hwpx_automation/office/authoring/design/profiles/application_form/template.hwpx +0 -0
  91. hwpx_automation/office/authoring/design/profiles/home_notice/fragments/body.xml +1 -0
  92. hwpx_automation/office/authoring/design/profiles/home_notice/fragments/heading.xml +1 -0
  93. hwpx_automation/office/authoring/design/profiles/home_notice/fragments/title.xml +1 -0
  94. hwpx_automation/office/authoring/design/profiles/home_notice/profile.json +24 -0
  95. hwpx_automation/office/authoring/design/profiles/home_notice/template.hwpx +0 -0
  96. hwpx_automation/office/authoring/design/profiles/official_notice/fragments/body.xml +1 -0
  97. hwpx_automation/office/authoring/design/profiles/official_notice/fragments/heading.xml +1 -0
  98. hwpx_automation/office/authoring/design/profiles/official_notice/fragments/info_table.xml +1 -0
  99. hwpx_automation/office/authoring/design/profiles/official_notice/fragments/title.xml +1 -0
  100. hwpx_automation/office/authoring/design/profiles/official_notice/profile.json +25 -0
  101. hwpx_automation/office/authoring/design/profiles/official_notice/template.hwpx +0 -0
  102. hwpx_automation/office/authoring/design/profiles/report/fragments/body.xml +1 -0
  103. hwpx_automation/office/authoring/design/profiles/report/fragments/heading.xml +1 -0
  104. hwpx_automation/office/authoring/design/profiles/report/fragments/info_table.xml +1 -0
  105. hwpx_automation/office/authoring/design/profiles/report/fragments/title.xml +1 -0
  106. hwpx_automation/office/authoring/design/profiles/report/profile.json +25 -0
  107. hwpx_automation/office/authoring/design/profiles/report/template.hwpx +0 -0
  108. hwpx_automation/office/authoring/design/validator.py +107 -0
  109. hwpx_automation/office/authoring/presets/__init__.py +22 -0
  110. hwpx_automation/office/authoring/presets/proposal.py +538 -0
  111. hwpx_automation/office/authoring/report_parser.py +141 -0
  112. hwpx_automation/office/authoring/style_profile.py +437 -0
  113. hwpx_automation/office/authoring/template_analyzer.py +657 -0
  114. hwpx_automation/office/compliance/__init__.py +38 -0
  115. hwpx_automation/office/compliance/official_lint.py +478 -0
  116. hwpx_automation/office/compliance/pii.py +388 -0
  117. hwpx_automation/office/document_ops/__init__.py +13 -0
  118. hwpx_automation/office/document_ops/comparison.py +62 -0
  119. hwpx_automation/office/document_ops/mail_merge.py +73 -0
  120. hwpx_automation/office/document_ops/redline.py +35 -0
  121. hwpx_automation/office/evalplan/__init__.py +36 -0
  122. hwpx_automation/office/evalplan/runtime.py +2762 -0
  123. hwpx_automation/office/exam/__init__.py +44 -0
  124. hwpx_automation/office/exam/compose.py +282 -0
  125. hwpx_automation/office/exam/ir.py +44 -0
  126. hwpx_automation/office/exam/measure.py +163 -0
  127. hwpx_automation/office/exam/parser.py +150 -0
  128. hwpx_automation/office/exam/profile.py +123 -0
  129. hwpx_automation/office/form_fill/__init__.py +66 -0
  130. hwpx_automation/office/form_fill/classification.py +108 -0
  131. hwpx_automation/office/form_fill/fill_residue.py +242 -0
  132. hwpx_automation/office/form_fill/fit/__init__.py +36 -0
  133. hwpx_automation/office/form_fill/fit/apply.py +24 -0
  134. hwpx_automation/office/form_fill/fit/engine.py +24 -0
  135. hwpx_automation/office/form_fill/fit/measure.py +50 -0
  136. hwpx_automation/office/form_fill/fit/policy.py +28 -0
  137. hwpx_automation/office/form_fill/fit/report.py +28 -0
  138. hwpx_automation/office/form_fill/fit/seal.py +457 -0
  139. hwpx_automation/office/form_fill/fit/wordbox.py +1343 -0
  140. hwpx_automation/office/form_fill/guidance.py +704 -0
  141. hwpx_automation/office/form_fill/quality.py +961 -0
  142. hwpx_automation/office/form_fill/split_run.py +333 -0
  143. hwpx_automation/office/form_fill/template_formfit.py +656 -0
  144. hwpx_automation/office/house_style/__init__.py +196 -0
  145. hwpx_automation/office/house_style/composition.py +68 -0
  146. hwpx_automation/office/house_style/data/bank.json +625 -0
  147. hwpx_automation/office/house_style/data/genres.json +43 -0
  148. hwpx_automation/office/quality/__init__.py +14 -0
  149. hwpx_automation/office/quality/page_guard.py +277 -0
  150. hwpx_automation/office/rendering/__init__.py +145 -0
  151. hwpx_automation/office/rendering/_hancom_open_rate.ps1 +374 -0
  152. hwpx_automation/office/rendering/_refresh_hwpx_mac.applescript +162 -0
  153. hwpx_automation/office/rendering/_render_hwpx.ps1 +72 -0
  154. hwpx_automation/office/rendering/_render_hwpx_mac.applescript +249 -0
  155. hwpx_automation/office/rendering/block_splits.py +76 -0
  156. hwpx_automation/office/rendering/detectors.py +151 -0
  157. hwpx_automation/office/rendering/diff.py +153 -0
  158. hwpx_automation/office/rendering/fixture_corpus.py +215 -0
  159. hwpx_automation/office/rendering/oracle.py +909 -0
  160. hwpx_automation/office/rendering/page_qa.py +245 -0
  161. hwpx_automation/office/rendering/qa_contracts.py +293 -0
  162. hwpx_automation/office/rendering/qa_metrics.py +241 -0
  163. hwpx_automation/office/rendering/worker.py +290 -0
  164. hwpx_automation/office/utilities/__init__.py +12 -0
  165. hwpx_automation/office/utilities/table_compute.py +477 -0
  166. hwpx_automation/ops_services/__init__.py +1 -0
  167. hwpx_automation/ops_services/_border_fill.py +283 -0
  168. hwpx_automation/ops_services/composition.py +55 -0
  169. hwpx_automation/ops_services/content_layout.py +322 -0
  170. hwpx_automation/ops_services/context.py +213 -0
  171. hwpx_automation/ops_services/form_fields.py +557 -0
  172. hwpx_automation/ops_services/media.py +178 -0
  173. hwpx_automation/ops_services/memo_style.py +477 -0
  174. hwpx_automation/ops_services/package_validation.py +166 -0
  175. hwpx_automation/ops_services/planning.py +201 -0
  176. hwpx_automation/ops_services/preview_export.py +585 -0
  177. hwpx_automation/ops_services/read_query.py +601 -0
  178. hwpx_automation/ops_services/save_policy.py +604 -0
  179. hwpx_automation/ops_services/tables.py +539 -0
  180. hwpx_automation/ops_services/transactions.py +616 -0
  181. hwpx_automation/preview_output_models.py +69 -0
  182. hwpx_automation/public-modules.json +206 -0
  183. hwpx_automation/py.typed +1 -0
  184. hwpx_automation/quality.py +351 -0
  185. hwpx_automation/quality_generation.py +725 -0
  186. hwpx_automation/runtime.py +321 -0
  187. hwpx_automation/runtime_services.py +100 -0
  188. hwpx_automation/server.py +259 -0
  189. hwpx_automation/storage.py +747 -0
  190. hwpx_automation/tool_bindings.py +170 -0
  191. hwpx_automation/tool_contract.py +982 -0
  192. hwpx_automation/upstream.py +755 -0
  193. hwpx_automation/utils/__init__.py +2 -0
  194. hwpx_automation/utils/helpers.py +29 -0
  195. hwpx_automation/visual_qa.py +667 -0
  196. hwpx_automation/workflow/__init__.py +55 -0
  197. hwpx_automation/workflow/adapters.py +482 -0
  198. hwpx_automation/workflow/dispatcher.py +213 -0
  199. hwpx_automation/workflow/models.py +243 -0
  200. hwpx_automation/workflow/policy.py +198 -0
  201. hwpx_automation/workflow/render_contracts.py +173 -0
  202. hwpx_automation/workflow/render_metrics.py +196 -0
  203. hwpx_automation/workflow/render_queue.py +482 -0
  204. hwpx_automation/workflow/render_security.py +172 -0
  205. hwpx_automation/workflow/render_transport.py +369 -0
  206. hwpx_automation/workflow/rendering.py +206 -0
  207. hwpx_automation/workflow/service.py +758 -0
  208. hwpx_automation/workflow/state_machine.py +65 -0
  209. hwpx_automation/workflow/store.py +747 -0
  210. hwpx_automation/workspace.py +1694 -0
  211. python_hwpx_automation-6.0.3.dist-info/METADATA +279 -0
  212. python_hwpx_automation-6.0.3.dist-info/RECORD +217 -0
  213. python_hwpx_automation-6.0.3.dist-info/WHEEL +5 -0
  214. python_hwpx_automation-6.0.3.dist-info/entry_points.txt +3 -0
  215. python_hwpx_automation-6.0.3.dist-info/licenses/LICENSE +178 -0
  216. python_hwpx_automation-6.0.3.dist-info/licenses/NOTICE +14 -0
  217. python_hwpx_automation-6.0.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,808 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """Strict v1 contracts for the semantic HWPX agent interface.
3
+
4
+ This module intentionally defines only public, JSON-serialisable semantics. It
5
+ does not expose lxml elements, package part paths, namespace URIs, XPath, or a
6
+ generic attribute mutation escape hatch. Projection, path evaluation, query,
7
+ and mutation execution are implemented in neighbouring modules after these
8
+ contracts are frozen.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import re
14
+ from collections.abc import Mapping, Sequence
15
+ from dataclasses import dataclass, field
16
+ from typing import Any
17
+
18
+ from hwpx.mutation_report import (
19
+ ChangedPart,
20
+ MutationReport,
21
+ PreservationCounts,
22
+ PreservationSummary,
23
+ verification_from_open_safety,
24
+ visual_value_from_status,
25
+ )
26
+
27
+ AGENT_NODE_SCHEMA = "hwpx.agent-node/v1"
28
+ AGENT_COMMAND_SCHEMA = "hwpx.agent-command/v1"
29
+ AGENT_BATCH_SCHEMA = "hwpx.agent-batch/v1"
30
+ AGENT_BATCH_RESULT_SCHEMA = "hwpx.agent-batch-result/v1"
31
+ AGENT_ERROR_SCHEMA = "hwpx.agent-error/v1"
32
+ AGENT_CATALOG_SCHEMA = "hwpx.agent-catalog/v1"
33
+
34
+ NODE_KINDS = (
35
+ "document",
36
+ "section",
37
+ "paragraph",
38
+ "run",
39
+ "table",
40
+ "row",
41
+ "cell",
42
+ "form-field",
43
+ "picture",
44
+ "memo",
45
+ "footnote",
46
+ "endnote",
47
+ "shape",
48
+ "unsupported",
49
+ )
50
+ STABILITY_LEVELS = ("native", "derived", "positional")
51
+ AGENT_OPERATIONS = ("set", "add", "remove", "move", "copy")
52
+
53
+ SELECTOR_KINDS = tuple(kind for kind in NODE_KINDS if kind != "unsupported")
54
+ SELECTOR_ATTRIBUTES = ("id", "name", "style", "type")
55
+ SELECTOR_FEATURES = (
56
+ "kind",
57
+ "exact-attribute",
58
+ "contains",
59
+ "direct-child",
60
+ )
61
+
62
+ MAX_VIEW_DEPTH = 8
63
+ MAX_CHILDREN_PER_NODE = 200
64
+ MAX_QUERY_RESULTS = 100
65
+ MAX_TEXT_CHARS = 4096
66
+ MAX_SELECTOR_CHARS = 512
67
+ MAX_COMMANDS = 100
68
+ MAX_PROPERTIES_PER_COMMAND = 32
69
+ MAX_JSON_DEPTH = 8
70
+
71
+ REVISION_PATTERN = re.compile(r"^sha256:[a-f0-9]{64}$")
72
+ COMMAND_ID_PATTERN = re.compile(r"^[A-Za-z][A-Za-z0-9_-]{0,31}$")
73
+ COMMAND_REF_PATTERN = re.compile(
74
+ r"^\$([A-Za-z][A-Za-z0-9_-]{0,31})\.(path|parentPath)$"
75
+ )
76
+ CANONICAL_PATH_PATTERN = re.compile(r"^/(?:[^\x00-\x1f]*)$")
77
+
78
+ ERROR_CODES = (
79
+ "invalid_syntax",
80
+ "unknown_kind",
81
+ "unknown_property",
82
+ "unsupported_operation",
83
+ "not_found",
84
+ "ambiguous_target",
85
+ "stale_revision",
86
+ "volatile_target",
87
+ "incompatible_parent",
88
+ "identity_collision",
89
+ "invariant_violation",
90
+ "unsupported_content",
91
+ "resource_limit",
92
+ "verification_failed",
93
+ "idempotency_conflict",
94
+ )
95
+ RECOVERABILITY = ("retryable", "needs-review", "terminal")
96
+
97
+ VERIFICATION_REQUIREMENTS = (
98
+ "package",
99
+ "reopen",
100
+ "openSafety",
101
+ "semanticDiff",
102
+ "bytePreservation",
103
+ "domain",
104
+ "realHancom",
105
+ )
106
+ QUALITY_MODES = ("transparent", "strict")
107
+ QUALITY_KEYS = frozenset(
108
+ {
109
+ "mode",
110
+ "renderCheck",
111
+ "xsdMode",
112
+ "overflowPolicy",
113
+ "layoutLint",
114
+ "preserveUnmodifiedParts",
115
+ "requireReferenceIntegrity",
116
+ }
117
+ )
118
+ _QUALITY_ENUM_VALUES = {
119
+ "mode": QUALITY_MODES,
120
+ "renderCheck": ("off", "auto", "required"),
121
+ "xsdMode": ("off", "lint"),
122
+ "overflowPolicy": ("fail", "warn", "truncate"),
123
+ "layoutLint": ("off", "warn", "strict"),
124
+ }
125
+ _QUALITY_BOOLEAN_KEYS = frozenset(
126
+ {"preserveUnmodifiedParts", "requireReferenceIntegrity"}
127
+ )
128
+
129
+ # The v1 public property vocabulary. P2's command catalog consumes this exact
130
+ # manifest. A property absent here cannot be set through generic commands.
131
+ NODE_PROPERTY_CATALOG_V1: dict[str, dict[str, tuple[str, ...]]] = {
132
+ "document": {
133
+ "readable": ("title", "author", "sectionCount", "paragraphCount", "tableCount"),
134
+ "editable": (),
135
+ "operations": ("add",),
136
+ },
137
+ "section": {
138
+ "readable": ("index", "partId", "paragraphCount", "pageWidthMm", "pageHeightMm"),
139
+ "editable": (),
140
+ "operations": ("add",),
141
+ },
142
+ "paragraph": {
143
+ "readable": (
144
+ "text",
145
+ "style",
146
+ "alignment",
147
+ "breakBefore",
148
+ "keepWithNext",
149
+ "lineSpacingPercent",
150
+ ),
151
+ "editable": (
152
+ "text",
153
+ "style",
154
+ "alignment",
155
+ "breakBefore",
156
+ "keepWithNext",
157
+ "lineSpacingPercent",
158
+ ),
159
+ "operations": AGENT_OPERATIONS,
160
+ },
161
+ "run": {
162
+ "readable": ("text", "bold", "italic", "underline", "fontName", "fontSizePt", "color"),
163
+ "editable": ("text", "bold", "italic", "underline", "fontName", "fontSizePt", "color"),
164
+ "operations": ("set", "add", "remove", "copy"),
165
+ },
166
+ "table": {
167
+ "readable": ("rowCount", "columnCount", "caption", "widthMm", "alignment"),
168
+ "editable": ("caption", "alignment"),
169
+ "operations": AGENT_OPERATIONS,
170
+ },
171
+ "row": {
172
+ "readable": ("index", "cellCount", "heightMm"),
173
+ "editable": ("heightMm",),
174
+ "operations": ("set", "add", "remove", "move", "copy"),
175
+ },
176
+ "cell": {
177
+ "readable": (
178
+ "text",
179
+ "row",
180
+ "column",
181
+ "rowSpan",
182
+ "columnSpan",
183
+ "verticalAlignment",
184
+ "backgroundColor",
185
+ ),
186
+ "editable": ("text", "verticalAlignment", "backgroundColor"),
187
+ "operations": ("set",),
188
+ },
189
+ "form-field": {
190
+ "readable": ("name", "value", "fieldType", "readOnly"),
191
+ "editable": ("value", "readOnly"),
192
+ "operations": ("set",),
193
+ },
194
+ "picture": {
195
+ "readable": ("name", "altText", "widthMm", "heightMm", "mediaType"),
196
+ "editable": ("altText",),
197
+ "operations": ("set", "remove", "move", "copy"),
198
+ },
199
+ "memo": {
200
+ "readable": ("text", "author"),
201
+ "editable": ("text",),
202
+ "operations": ("set", "remove", "copy"),
203
+ },
204
+ "footnote": {
205
+ "readable": ("text",),
206
+ "editable": ("text",),
207
+ "operations": ("set", "remove", "copy"),
208
+ },
209
+ "endnote": {
210
+ "readable": ("text",),
211
+ "editable": ("text",),
212
+ "operations": ("set", "remove", "copy"),
213
+ },
214
+ "shape": {
215
+ "readable": ("shapeType", "name", "altText", "xMm", "yMm", "widthMm", "heightMm"),
216
+ "editable": ("altText",),
217
+ "operations": ("set", "remove", "move", "copy"),
218
+ },
219
+ "unsupported": {"readable": ("localName", "reason"), "editable": (), "operations": ()},
220
+ }
221
+
222
+ _RAW_KEYS = frozenset(
223
+ {"xml", "raw", "rawxml", "xpath", "namespace", "namespaceuri", "packagepath", "partpath"}
224
+ )
225
+
226
+
227
+ class AgentContractError(ValueError):
228
+ """A stable-code contract validation error."""
229
+
230
+ def __init__(self, code: str, message: str, *, target: str | None = None) -> None:
231
+ if code not in ERROR_CODES:
232
+ raise ValueError(f"unknown agent error code: {code}")
233
+ super().__init__(message)
234
+ self.code = code
235
+ self.target = target
236
+
237
+
238
+ def _require_mapping(value: object, name: str) -> Mapping[str, Any]:
239
+ if not isinstance(value, Mapping):
240
+ raise AgentContractError("invalid_syntax", f"{name} must be an object", target=name)
241
+ return value
242
+
243
+
244
+ def _require_exact_keys(
245
+ value: Mapping[str, Any],
246
+ *,
247
+ required: set[str] | frozenset[str],
248
+ optional: set[str] | frozenset[str] = frozenset(),
249
+ name: str,
250
+ ) -> None:
251
+ missing = required - set(value)
252
+ extra = set(value) - required - optional
253
+ if missing or extra:
254
+ raise AgentContractError(
255
+ "invalid_syntax",
256
+ f"{name} fields mismatch (missing={sorted(missing)}, extra={sorted(extra)})",
257
+ target=name,
258
+ )
259
+
260
+
261
+ def _validate_json_value(value: object, *, name: str, depth: int = 0) -> None:
262
+ if depth > MAX_JSON_DEPTH:
263
+ raise AgentContractError("resource_limit", f"{name} exceeds JSON depth limit", target=name)
264
+ if value is None or isinstance(value, (bool, int, float)):
265
+ return
266
+ if isinstance(value, str):
267
+ if len(value) > MAX_TEXT_CHARS:
268
+ raise AgentContractError("resource_limit", f"{name} string is too long", target=name)
269
+ return
270
+ if isinstance(value, Mapping):
271
+ for key, child in value.items():
272
+ key_text = str(key)
273
+ normalized = re.sub(r"[^a-z]", "", key_text.casefold())
274
+ if normalized in _RAW_KEYS:
275
+ raise AgentContractError(
276
+ "unknown_property",
277
+ f"{name} contains forbidden raw/package property {key_text!r}",
278
+ target=f"{name}.{key_text}",
279
+ )
280
+ _validate_json_value(child, name=f"{name}.{key_text}", depth=depth + 1)
281
+ return
282
+ if isinstance(value, (list, tuple)):
283
+ for index, child in enumerate(value):
284
+ _validate_json_value(child, name=f"{name}[{index}]", depth=depth + 1)
285
+ return
286
+ raise AgentContractError(
287
+ "invalid_syntax", f"{name} is not JSON-serialisable", target=name
288
+ )
289
+
290
+
291
+ def _validate_path_or_reference(value: object, name: str) -> str:
292
+ text = str(value or "")
293
+ if COMMAND_REF_PATTERN.fullmatch(text):
294
+ return text
295
+ if not CANONICAL_PATH_PATTERN.fullmatch(text):
296
+ raise AgentContractError(
297
+ "invalid_syntax", f"{name} must be an absolute semantic path or command reference", target=name
298
+ )
299
+ if len(text) > MAX_SELECTOR_CHARS * 2:
300
+ raise AgentContractError("resource_limit", f"{name} is too long", target=name)
301
+ return text
302
+
303
+
304
+ def _validate_properties(value: object, name: str) -> dict[str, Any]:
305
+ props = dict(_require_mapping(value, name))
306
+ if not props:
307
+ raise AgentContractError("invalid_syntax", f"{name} cannot be empty", target=name)
308
+ if len(props) > MAX_PROPERTIES_PER_COMMAND:
309
+ raise AgentContractError("resource_limit", f"{name} has too many properties", target=name)
310
+ _validate_json_value(props, name=name)
311
+ return props
312
+
313
+
314
+ def _validate_position(value: object, name: str = "command.position") -> dict[str, Any]:
315
+ position = dict(_require_mapping(value, name))
316
+ mode = str(position.get("mode", ""))
317
+ if mode in {"append", "prepend"}:
318
+ _require_exact_keys(position, required={"mode"}, name=name)
319
+ return {"mode": mode}
320
+ if mode == "index":
321
+ _require_exact_keys(position, required={"mode", "index"}, name=name)
322
+ index = position["index"]
323
+ if isinstance(index, bool) or not isinstance(index, int) or index < 1:
324
+ raise AgentContractError(
325
+ "invalid_syntax", f"{name}.index must be a one-based integer", target=f"{name}.index"
326
+ )
327
+ return {"mode": mode, "index": index}
328
+ if mode in {"before", "after"}:
329
+ _require_exact_keys(position, required={"mode", "path"}, name=name)
330
+ return {"mode": mode, "path": _validate_path_or_reference(position["path"], f"{name}.path")}
331
+ raise AgentContractError(
332
+ "invalid_syntax", f"{name}.mode must be append, prepend, index, before, or after", target=f"{name}.mode"
333
+ )
334
+
335
+
336
+ def _validate_quality(value: object) -> str | dict[str, Any] | None:
337
+ if value is None:
338
+ return None
339
+ if isinstance(value, str):
340
+ if value not in QUALITY_MODES:
341
+ raise AgentContractError("invalid_syntax", "batch.quality mode is unsupported", target="batch.quality")
342
+ return value
343
+ quality = dict(_require_mapping(value, "batch.quality"))
344
+ extra = set(quality) - QUALITY_KEYS
345
+ if extra:
346
+ raise AgentContractError(
347
+ "unknown_property",
348
+ f"batch.quality contains unknown fields: {sorted(extra)}",
349
+ target="batch.quality",
350
+ )
351
+ for name, choices in _QUALITY_ENUM_VALUES.items():
352
+ if name in quality and quality[name] not in choices:
353
+ raise AgentContractError(
354
+ "invalid_syntax",
355
+ f"batch.quality.{name} is unsupported",
356
+ target=f"batch.quality.{name}",
357
+ )
358
+ for name in _QUALITY_BOOLEAN_KEYS:
359
+ if name in quality and not isinstance(quality[name], bool):
360
+ raise AgentContractError(
361
+ "invalid_syntax",
362
+ f"batch.quality.{name} must be boolean",
363
+ target=f"batch.quality.{name}",
364
+ )
365
+ _validate_json_value(quality, name="batch.quality")
366
+ return quality
367
+
368
+
369
+ @dataclass(frozen=True, slots=True)
370
+ class AgentNode:
371
+ """A bounded semantic node resolved against one document revision."""
372
+
373
+ kind: str
374
+ path: str
375
+ stable_id: str | None
376
+ stability: str
377
+ summary: Mapping[str, Any]
378
+ child_count: int
379
+ children: tuple["AgentNode", ...] = ()
380
+ unsupported_child_count: int = 0
381
+ truncated_child_count: int = 0
382
+ readable_properties: tuple[str, ...] = ()
383
+ editable_properties: tuple[str, ...] = ()
384
+ operations: tuple[str, ...] = ()
385
+ revision: str = ""
386
+
387
+ def __post_init__(self) -> None:
388
+ if self.kind not in NODE_KINDS:
389
+ raise AgentContractError("unknown_kind", f"unknown node kind: {self.kind}", target="node.kind")
390
+ if not CANONICAL_PATH_PATTERN.fullmatch(self.path):
391
+ raise AgentContractError(
392
+ "invalid_syntax", "node.path must be an absolute canonical path", target="node.path"
393
+ )
394
+ if self.stability not in STABILITY_LEVELS:
395
+ raise AgentContractError("invalid_syntax", "unknown node stability", target="node.stability")
396
+ if self.stability == "positional" and self.stable_id is not None:
397
+ raise AgentContractError(
398
+ "invariant_violation", "positional nodes cannot claim stableId", target="node.stableId"
399
+ )
400
+ if self.stability != "positional" and not self.stable_id:
401
+ raise AgentContractError(
402
+ "invariant_violation", "native/derived nodes require stableId", target="node.stableId"
403
+ )
404
+ if self.stable_id is not None and (not self.stable_id or len(self.stable_id) > 256):
405
+ raise AgentContractError("resource_limit", "node.stableId is invalid", target="node.stableId")
406
+ for name, value in (
407
+ ("childCount", self.child_count),
408
+ ("unsupportedChildCount", self.unsupported_child_count),
409
+ ("truncatedChildCount", self.truncated_child_count),
410
+ ):
411
+ if isinstance(value, bool) or not isinstance(value, int) or value < 0:
412
+ raise AgentContractError("invalid_syntax", f"node.{name} must be non-negative", target=f"node.{name}")
413
+ if len(self.children) > MAX_CHILDREN_PER_NODE:
414
+ raise AgentContractError("resource_limit", "node children exceed limit", target="node.children")
415
+ if (
416
+ len(self.children)
417
+ + self.unsupported_child_count
418
+ + self.truncated_child_count
419
+ != self.child_count
420
+ ):
421
+ raise AgentContractError(
422
+ "invariant_violation", "node child coverage must equal childCount", target="node.children"
423
+ )
424
+ if not REVISION_PATTERN.fullmatch(self.revision):
425
+ raise AgentContractError("invalid_syntax", "node.revision must be sha256", target="node.revision")
426
+ _validate_json_value(self.summary, name="node.summary")
427
+ catalog = NODE_PROPERTY_CATALOG_V1[self.kind]
428
+ if tuple(self.readable_properties) != tuple(catalog["readable"]):
429
+ raise AgentContractError(
430
+ "invariant_violation", "node readable properties do not match v1 catalog", target="node.readableProperties"
431
+ )
432
+ if tuple(self.editable_properties) != tuple(catalog["editable"]):
433
+ raise AgentContractError(
434
+ "invariant_violation", "node editable properties do not match v1 catalog", target="node.editableProperties"
435
+ )
436
+ if tuple(self.operations) != tuple(catalog["operations"]):
437
+ raise AgentContractError(
438
+ "invariant_violation", "node operations do not match v1 catalog", target="node.operations"
439
+ )
440
+
441
+ @property
442
+ def volatile_path(self) -> bool:
443
+ return self.stability == "positional"
444
+
445
+ def to_dict(self) -> dict[str, Any]:
446
+ return {
447
+ "schemaVersion": AGENT_NODE_SCHEMA,
448
+ "kind": self.kind,
449
+ "path": self.path,
450
+ "stableId": self.stable_id,
451
+ "stability": self.stability,
452
+ "volatilePath": self.volatile_path,
453
+ "summary": dict(self.summary),
454
+ "childCount": self.child_count,
455
+ "children": [child.to_dict() for child in self.children],
456
+ "coverage": {
457
+ "supportedChildren": len(self.children),
458
+ "unsupportedChildren": self.unsupported_child_count,
459
+ "truncatedChildren": self.truncated_child_count,
460
+ },
461
+ "readableProperties": list(self.readable_properties),
462
+ "editableProperties": list(self.editable_properties),
463
+ "operations": list(self.operations),
464
+ "revision": self.revision,
465
+ }
466
+
467
+
468
+ @dataclass(frozen=True, slots=True)
469
+ class AgentError:
470
+ code: str
471
+ message: str
472
+ target: str | None = None
473
+ recoverability: str = "terminal"
474
+ suggestion: str | None = None
475
+ valid_values: tuple[str, ...] = ()
476
+
477
+ def __post_init__(self) -> None:
478
+ if self.code not in ERROR_CODES:
479
+ raise ValueError(f"unknown agent error code: {self.code}")
480
+ if self.recoverability not in RECOVERABILITY:
481
+ raise ValueError(f"unknown recoverability: {self.recoverability}")
482
+ if not self.message or len(self.message) > MAX_TEXT_CHARS:
483
+ raise ValueError("agent error message is empty or too long")
484
+
485
+ def to_dict(self) -> dict[str, Any]:
486
+ return {
487
+ "schemaVersion": AGENT_ERROR_SCHEMA,
488
+ "code": self.code,
489
+ "message": self.message,
490
+ "target": self.target,
491
+ "recoverability": self.recoverability,
492
+ "suggestion": self.suggestion,
493
+ "validValues": list(self.valid_values),
494
+ }
495
+
496
+
497
+ def validate_agent_command(value: Mapping[str, Any]) -> dict[str, Any]:
498
+ """Validate and normalize one strict op-specific command union."""
499
+
500
+ raw = dict(_require_mapping(value, "command"))
501
+ command_id = str(raw.get("commandId", ""))
502
+ if not COMMAND_ID_PATTERN.fullmatch(command_id):
503
+ raise AgentContractError("invalid_syntax", "commandId is invalid", target="command.commandId")
504
+ op = str(raw.get("op", ""))
505
+ if op not in AGENT_OPERATIONS:
506
+ raise AgentContractError("unsupported_operation", f"unsupported command op: {op}", target="command.op")
507
+
508
+ normalized: dict[str, Any] = {
509
+ "schemaVersion": AGENT_COMMAND_SCHEMA,
510
+ "commandId": command_id,
511
+ "op": op,
512
+ }
513
+ if op == "set":
514
+ _require_exact_keys(raw, required={"commandId", "op", "path", "properties"}, name="command")
515
+ normalized["path"] = _validate_path_or_reference(raw["path"], "command.path")
516
+ normalized["properties"] = _validate_properties(raw["properties"], "command.properties")
517
+ elif op == "add":
518
+ _require_exact_keys(
519
+ raw,
520
+ required={"commandId", "op", "parent", "kind", "properties"},
521
+ optional={"position"},
522
+ name="command",
523
+ )
524
+ kind = str(raw["kind"])
525
+ if kind not in NODE_KINDS or kind in {"document", "unsupported"}:
526
+ raise AgentContractError("unknown_kind", f"cannot add node kind: {kind}", target="command.kind")
527
+ normalized["parent"] = _validate_path_or_reference(raw["parent"], "command.parent")
528
+ normalized["kind"] = kind
529
+ normalized["properties"] = _validate_properties(raw["properties"], "command.properties")
530
+ normalized["position"] = _validate_position(raw.get("position", {"mode": "append"}))
531
+ elif op == "remove":
532
+ _require_exact_keys(raw, required={"commandId", "op", "path"}, name="command")
533
+ normalized["path"] = _validate_path_or_reference(raw["path"], "command.path")
534
+ else:
535
+ _require_exact_keys(
536
+ raw,
537
+ required={"commandId", "op", "path", "parent"},
538
+ optional={"position"},
539
+ name="command",
540
+ )
541
+ normalized["path"] = _validate_path_or_reference(raw["path"], "command.path")
542
+ normalized["parent"] = _validate_path_or_reference(raw["parent"], "command.parent")
543
+ normalized["position"] = _validate_position(raw.get("position", {"mode": "append"}))
544
+ return normalized
545
+
546
+
547
+ def validate_agent_batch(value: Mapping[str, Any]) -> dict[str, Any]:
548
+ """Validate a complete atomic batch envelope without touching a document."""
549
+
550
+ raw = dict(_require_mapping(value, "batch"))
551
+ required = {
552
+ "schemaVersion",
553
+ "input",
554
+ "output",
555
+ "commands",
556
+ "expectedRevision",
557
+ "idempotencyKey",
558
+ "dryRun",
559
+ "quality",
560
+ "verificationRequirements",
561
+ }
562
+ _require_exact_keys(raw, required=required, name="batch")
563
+ if raw["schemaVersion"] != AGENT_BATCH_SCHEMA:
564
+ raise AgentContractError("invalid_syntax", "unsupported batch schemaVersion", target="batch.schemaVersion")
565
+
566
+ input_ref = dict(_require_mapping(raw["input"], "batch.input"))
567
+ _require_exact_keys(input_ref, required={"filename"}, name="batch.input")
568
+ if not str(input_ref["filename"]).strip():
569
+ raise AgentContractError("invalid_syntax", "batch.input.filename is required", target="batch.input.filename")
570
+
571
+ output_ref = dict(_require_mapping(raw["output"], "batch.output"))
572
+ _require_exact_keys(output_ref, required={"filename", "overwrite"}, name="batch.output")
573
+ if not str(output_ref["filename"]).strip() or not isinstance(output_ref["overwrite"], bool):
574
+ raise AgentContractError("invalid_syntax", "batch.output is invalid", target="batch.output")
575
+
576
+ commands_value = raw["commands"]
577
+ if isinstance(commands_value, (str, bytes)) or not isinstance(commands_value, Sequence):
578
+ raise AgentContractError("invalid_syntax", "batch.commands must be an array", target="batch.commands")
579
+ if not commands_value or len(commands_value) > MAX_COMMANDS:
580
+ raise AgentContractError("resource_limit", "batch.commands count is out of bounds", target="batch.commands")
581
+ commands = [validate_agent_command(_require_mapping(item, "command")) for item in commands_value]
582
+ command_ids = [command["commandId"] for command in commands]
583
+ if len(command_ids) != len(set(command_ids)):
584
+ raise AgentContractError("invariant_violation", "batch commandId values must be unique", target="batch.commands")
585
+ for index, command in enumerate(commands):
586
+ for key in ("path", "parent"):
587
+ target = command.get(key)
588
+ match = COMMAND_REF_PATTERN.fullmatch(str(target or ""))
589
+ if not match:
590
+ continue
591
+ referenced = match.group(1)
592
+ if referenced not in command_ids[:index]:
593
+ raise AgentContractError(
594
+ "invalid_syntax",
595
+ f"command reference {referenced!r} must name an earlier command",
596
+ target=f"batch.commands[{index}].{key}",
597
+ )
598
+
599
+ expected_revision = raw["expectedRevision"]
600
+ if expected_revision is not None and not REVISION_PATTERN.fullmatch(str(expected_revision)):
601
+ raise AgentContractError("invalid_syntax", "expectedRevision must be sha256 or null", target="batch.expectedRevision")
602
+ idempotency_key = raw["idempotencyKey"]
603
+ if idempotency_key is not None and not (1 <= len(str(idempotency_key)) <= 128):
604
+ raise AgentContractError("resource_limit", "idempotencyKey length is invalid", target="batch.idempotencyKey")
605
+ if not isinstance(raw["dryRun"], bool):
606
+ raise AgentContractError("invalid_syntax", "dryRun must be boolean", target="batch.dryRun")
607
+
608
+ requirements = raw["verificationRequirements"]
609
+ if isinstance(requirements, (str, bytes)) or not isinstance(requirements, Sequence):
610
+ raise AgentContractError(
611
+ "invalid_syntax", "verificationRequirements must be an array", target="batch.verificationRequirements"
612
+ )
613
+ unknown_requirements = sorted(set(requirements) - set(VERIFICATION_REQUIREMENTS))
614
+ if unknown_requirements:
615
+ raise AgentContractError(
616
+ "invalid_syntax",
617
+ f"unknown verification requirements: {unknown_requirements}",
618
+ target="batch.verificationRequirements",
619
+ )
620
+
621
+ return {
622
+ "schemaVersion": AGENT_BATCH_SCHEMA,
623
+ "input": {"filename": str(input_ref["filename"])},
624
+ "output": {"filename": str(output_ref["filename"]), "overwrite": output_ref["overwrite"]},
625
+ "commands": commands,
626
+ "expectedRevision": None if expected_revision is None else str(expected_revision),
627
+ "idempotencyKey": None if idempotency_key is None else str(idempotency_key),
628
+ "dryRun": raw["dryRun"],
629
+ "quality": _validate_quality(raw["quality"]),
630
+ "verificationRequirements": list(dict.fromkeys(str(item) for item in requirements)),
631
+ }
632
+
633
+
634
+ @dataclass(frozen=True, slots=True)
635
+ class AgentBatchResult:
636
+ ok: bool
637
+ rolled_back: bool
638
+ dry_run: bool
639
+ input_revision: str
640
+ document_revision: str
641
+ output_filename: str
642
+ command_results: tuple[Mapping[str, Any], ...] = ()
643
+ semantic_diff: Mapping[str, Any] = field(default_factory=dict)
644
+ verification_report: Mapping[str, Any] = field(default_factory=dict)
645
+ error: AgentError | None = None
646
+
647
+ def __post_init__(self) -> None:
648
+ for name, revision in (
649
+ ("inputRevision", self.input_revision),
650
+ ("documentRevision", self.document_revision),
651
+ ):
652
+ if not REVISION_PATTERN.fullmatch(revision):
653
+ raise ValueError(f"{name} must be sha256")
654
+ if self.ok and self.rolled_back:
655
+ raise ValueError("successful batch cannot be rolled back")
656
+ if not self.ok and self.error is None:
657
+ raise ValueError("failed batch requires a structured error")
658
+ if len(self.command_results) > MAX_COMMANDS:
659
+ raise ValueError("too many command results")
660
+ _validate_json_value(self.command_results, name="result.commandResults")
661
+ _validate_json_value(self.semantic_diff, name="result.semanticDiff")
662
+ _validate_json_value(self.verification_report, name="result.verificationReport")
663
+
664
+ def to_dict(self) -> dict[str, Any]:
665
+ return {
666
+ "schemaVersion": AGENT_BATCH_RESULT_SCHEMA,
667
+ "ok": self.ok,
668
+ "rolledBack": self.rolled_back,
669
+ "dryRun": self.dry_run,
670
+ "inputRevision": self.input_revision,
671
+ "documentRevision": self.document_revision,
672
+ "output": {"filename": self.output_filename},
673
+ "commandResults": [dict(item) for item in self.command_results],
674
+ "semanticDiff": dict(self.semantic_diff),
675
+ "verificationReport": dict(self.verification_report),
676
+ "error": None if self.error is None else self.error.to_dict(),
677
+ }
678
+
679
+ def as_mutation_report(self) -> MutationReport:
680
+ """Project this transaction result onto ``hwpx.mutation-report/v1`` (specs/032
681
+ §3). Additive — the fields above are untouched.
682
+
683
+ The agent write is a family-A rebuild (``document.to_bytes()``), so
684
+ ``actualMode`` is ``"rebuild"`` and changed parts carry no ranges (a rebuilt
685
+ part is re-serialized whole). Preservation and verification are read from the
686
+ already-measured ``verification_report`` (``bytePreservation`` = the shared
687
+ ``_member_diff``, ``openSafety``, ``realHancom``); layers that report never
688
+ measured — local ZIP records, an absent oracle — stay zero-verified /
689
+ ``not_performed`` rather than being promoted to a pass.
690
+ """
691
+
692
+ report = self.verification_report
693
+ byte = report.get("bytePreservation") or {}
694
+ changed_names = [
695
+ *byte.get("changedMembers", ()),
696
+ *byte.get("addedMembers", ()),
697
+ *byte.get("removedMembers", ()),
698
+ ]
699
+ changed_parts = tuple(
700
+ ChangedPart(path=str(name), reason="dirty-part", ranges=None)
701
+ for name in changed_names
702
+ )
703
+ unchanged = byte.get("unchangedMemberCount")
704
+ preservation = PreservationSummary(
705
+ untouched_part_payloads=PreservationCounts(
706
+ verified=unchanged if isinstance(unchanged, int) else 0, changed=0
707
+ ),
708
+ untouched_local_zip_records=PreservationCounts(verified=0, changed=0),
709
+ whole_package_identical=bool(byte) and not changed_names,
710
+ )
711
+ real_hancom = report.get("realHancom") or {}
712
+ verification = verification_from_open_safety(
713
+ report.get("openSafety"),
714
+ visual=visual_value_from_status(real_hancom.get("status")),
715
+ )
716
+ return MutationReport(
717
+ requested_mode="rebuild",
718
+ actual_mode="rebuild",
719
+ fallback_used=False,
720
+ changed_parts=changed_parts,
721
+ preservation=preservation,
722
+ verification=verification,
723
+ path=self.output_filename,
724
+ )
725
+
726
+
727
+ def agent_contract_manifest() -> dict[str, Any]:
728
+ """Return the deterministic, inspectable v1 contract manifest."""
729
+
730
+ return {
731
+ "schemaVersion": AGENT_CATALOG_SCHEMA,
732
+ "schemas": {
733
+ "node": AGENT_NODE_SCHEMA,
734
+ "command": AGENT_COMMAND_SCHEMA,
735
+ "batch": AGENT_BATCH_SCHEMA,
736
+ "batchResult": AGENT_BATCH_RESULT_SCHEMA,
737
+ "error": AGENT_ERROR_SCHEMA,
738
+ },
739
+ "limits": {
740
+ "maxViewDepth": MAX_VIEW_DEPTH,
741
+ "maxChildrenPerNode": MAX_CHILDREN_PER_NODE,
742
+ "maxQueryResults": MAX_QUERY_RESULTS,
743
+ "maxTextChars": MAX_TEXT_CHARS,
744
+ "maxSelectorChars": MAX_SELECTOR_CHARS,
745
+ "maxCommands": MAX_COMMANDS,
746
+ "maxPropertiesPerCommand": MAX_PROPERTIES_PER_COMMAND,
747
+ },
748
+ "path": {
749
+ "root": "/",
750
+ "externalIndexBase": 1,
751
+ "stabilityLevels": list(STABILITY_LEVELS),
752
+ "commandReference": "$<commandId>.path",
753
+ },
754
+ "selector": {
755
+ "features": list(SELECTOR_FEATURES),
756
+ "kinds": list(SELECTOR_KINDS),
757
+ "attributes": list(SELECTOR_ATTRIBUTES),
758
+ "resultOrder": "document",
759
+ "xpath": False,
760
+ "regex": False,
761
+ "rawXml": False,
762
+ },
763
+ "operations": list(AGENT_OPERATIONS),
764
+ "nodeKinds": {
765
+ kind: {
766
+ "readableProperties": list(NODE_PROPERTY_CATALOG_V1[kind]["readable"]),
767
+ "editableProperties": list(NODE_PROPERTY_CATALOG_V1[kind]["editable"]),
768
+ "operations": list(NODE_PROPERTY_CATALOG_V1[kind]["operations"]),
769
+ }
770
+ for kind in NODE_KINDS
771
+ },
772
+ "verificationRequirements": list(VERIFICATION_REQUIREMENTS),
773
+ "qualityModes": list(QUALITY_MODES),
774
+ "errorCodes": list(ERROR_CODES),
775
+ "recoverability": list(RECOVERABILITY),
776
+ }
777
+
778
+
779
+ __all__ = [
780
+ "AGENT_BATCH_RESULT_SCHEMA",
781
+ "AGENT_BATCH_SCHEMA",
782
+ "AGENT_CATALOG_SCHEMA",
783
+ "AGENT_COMMAND_SCHEMA",
784
+ "AGENT_ERROR_SCHEMA",
785
+ "AGENT_NODE_SCHEMA",
786
+ "AGENT_OPERATIONS",
787
+ "AgentBatchResult",
788
+ "AgentContractError",
789
+ "AgentError",
790
+ "AgentNode",
791
+ "ERROR_CODES",
792
+ "MAX_CHILDREN_PER_NODE",
793
+ "MAX_COMMANDS",
794
+ "MAX_PROPERTIES_PER_COMMAND",
795
+ "MAX_QUERY_RESULTS",
796
+ "MAX_SELECTOR_CHARS",
797
+ "MAX_TEXT_CHARS",
798
+ "MAX_VIEW_DEPTH",
799
+ "NODE_KINDS",
800
+ "NODE_PROPERTY_CATALOG_V1",
801
+ "SELECTOR_ATTRIBUTES",
802
+ "SELECTOR_FEATURES",
803
+ "STABILITY_LEVELS",
804
+ "VERIFICATION_REQUIREMENTS",
805
+ "agent_contract_manifest",
806
+ "validate_agent_batch",
807
+ "validate_agent_command",
808
+ ]