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,263 @@
1
+ {
2
+ "schemaVersion": "python-hwpx-automation.identity/v1",
3
+ "product": "python-hwpx-automation",
4
+ "releaseMajor": 6,
5
+ "releaseState": {
6
+ "status": "release-approved",
7
+ "candidate": {
8
+ "pythonHwpx": "5.0.1",
9
+ "canonicalDistribution": "python-hwpx-automation",
10
+ "canonicalAutomation": "6.0.3",
11
+ "compatibilityDistribution": "hwpx-mcp-server",
12
+ "compatibility": "6.0.3",
13
+ "plugin": "1.0.0",
14
+ "contractHash": "0ce938371f0b55a6"
15
+ },
16
+ "currentPublic": {
17
+ "pythonHwpx": "4.2.0",
18
+ "primaryDistribution": "hwpx-mcp-server",
19
+ "primaryApplication": "5.1.0",
20
+ "plugin": "0.8.0",
21
+ "contractHash": "429cb6706323e762"
22
+ },
23
+ "promotionGate": "Three states are mandatory: unreleased-candidate while auditing; release-approved only after separate owner approval and while currentPublic still names the previously observed coherent stack; released only in a follow-up commit after remote truth is observed for core, canonical automation, the compatibility distribution, the plugin GitHub release, the marketplace entry, and a real marketplace install. The automation tag workflow publishes only release-approved, leaves currentPublic unchanged, and hands an attached receipt to plugin publication."
24
+ },
25
+ "identifiers": [
26
+ {
27
+ "surface": "repository",
28
+ "value": "airmang/python-hwpx-automation",
29
+ "classification": "canonical"
30
+ },
31
+ {
32
+ "surface": "distribution",
33
+ "value": "python-hwpx-automation",
34
+ "classification": "canonical"
35
+ },
36
+ {
37
+ "surface": "import-namespace",
38
+ "value": "hwpx_automation",
39
+ "classification": "canonical"
40
+ },
41
+ {
42
+ "surface": "task-console",
43
+ "value": "hwpx",
44
+ "classification": "canonical"
45
+ },
46
+ {
47
+ "surface": "mcp-console",
48
+ "value": "hwpx-automation-mcp",
49
+ "classification": "canonical"
50
+ },
51
+ {
52
+ "surface": "fastmcp-server-info-name",
53
+ "value": "python-hwpx-automation",
54
+ "classification": "canonical"
55
+ },
56
+ {
57
+ "surface": "host-config-key",
58
+ "value": "hwpx",
59
+ "classification": "host-local-alias"
60
+ },
61
+ {
62
+ "surface": "environment-prefix",
63
+ "value": "HWPX_AUTOMATION_",
64
+ "classification": "canonical"
65
+ },
66
+ {
67
+ "surface": "distribution",
68
+ "value": "hwpx-mcp-server",
69
+ "classification": "compatibility"
70
+ },
71
+ {
72
+ "surface": "import-namespace",
73
+ "value": "hwpx_mcp_server",
74
+ "classification": "compatibility"
75
+ },
76
+ {
77
+ "surface": "mcp-console",
78
+ "value": "hwpx-mcp-server",
79
+ "classification": "compatibility"
80
+ },
81
+ {
82
+ "surface": "environment-prefix",
83
+ "value": "HWPX_MCP_",
84
+ "classification": "compatibility"
85
+ },
86
+ {
87
+ "surface": "capability-version-field",
88
+ "value": "versions.automation",
89
+ "classification": "canonical"
90
+ },
91
+ {
92
+ "surface": "capability-version-field",
93
+ "value": "versions.mcp",
94
+ "classification": "compatibility-preserved"
95
+ },
96
+ {
97
+ "surface": "capability-floor-field",
98
+ "value": "minAutomationVersion",
99
+ "classification": "canonical"
100
+ },
101
+ {
102
+ "surface": "capability-floor-field",
103
+ "value": "minMcpVersion",
104
+ "classification": "compatibility-preserved"
105
+ },
106
+ {
107
+ "surface": "contract-floor-constant",
108
+ "value": "MIN_AUTOMATION_VERSION",
109
+ "classification": "canonical"
110
+ },
111
+ {
112
+ "surface": "contract-floor-constant",
113
+ "value": "MIN_MCP_VERSION",
114
+ "classification": "compatibility-preserved"
115
+ },
116
+ {
117
+ "surface": "protocol-error-schema",
118
+ "value": "hwpx.mcp-error/v1",
119
+ "classification": "compatibility-preserved"
120
+ },
121
+ {
122
+ "surface": "frozen-receipt-schema",
123
+ "value": "hwpx-mcp.authoring-runtime-owner/v1",
124
+ "classification": "compatibility-preserved"
125
+ },
126
+ {
127
+ "surface": "frozen-receipt-schema",
128
+ "value": "hwpx-mcp.compliance-quality-utilities-owner/v1",
129
+ "classification": "compatibility-preserved"
130
+ },
131
+ {
132
+ "surface": "frozen-receipt-schema",
133
+ "value": "hwpx-mcp.document-operations-owner/v1",
134
+ "classification": "compatibility-preserved"
135
+ },
136
+ {
137
+ "surface": "frozen-receipt-schema",
138
+ "value": "hwpx-mcp.evalplan-runtime-owner/v1",
139
+ "classification": "compatibility-preserved"
140
+ },
141
+ {
142
+ "surface": "frozen-receipt-schema",
143
+ "value": "hwpx-mcp.exam-runtime-owner/v1",
144
+ "classification": "compatibility-preserved"
145
+ },
146
+ {
147
+ "surface": "frozen-receipt-schema",
148
+ "value": "hwpx-mcp.form-fill-runtime-owner/v1",
149
+ "classification": "compatibility-preserved"
150
+ },
151
+ {
152
+ "surface": "frozen-receipt-schema",
153
+ "value": "hwpx-mcp.form-fill-runtime-parity/v1",
154
+ "classification": "compatibility-preserved"
155
+ },
156
+ {
157
+ "surface": "frozen-receipt-schema",
158
+ "value": "hwpx-mcp.visual-runtime-owner/v1",
159
+ "classification": "compatibility-preserved"
160
+ },
161
+ {
162
+ "surface": "frozen-parity-receipt-field",
163
+ "value": "mcpRuntimeMembers",
164
+ "classification": "compatibility-preserved"
165
+ },
166
+ {
167
+ "surface": "workflow-state-environment",
168
+ "value": "HWPX_AUTOMATION_WORKFLOW_STORE",
169
+ "classification": "canonical"
170
+ },
171
+ {
172
+ "surface": "workflow-state-environment",
173
+ "value": "HWPX_WORKFLOW_STORE",
174
+ "classification": "compatibility-preserved"
175
+ },
176
+ {
177
+ "surface": "workflow-state-path",
178
+ "value": "${XDG_STATE_HOME:-~/.local/state}/hwpx-mcp-server/workflows.sqlite3",
179
+ "classification": "compatibility-preserved"
180
+ },
181
+ {
182
+ "surface": "render-queue-principal",
183
+ "value": "hwpx-mcp-server",
184
+ "classification": "compatibility-preserved"
185
+ },
186
+ {
187
+ "surface": "render-integration-environment",
188
+ "value": "HWPX_RENDER_QUEUE_ROOT",
189
+ "classification": "integration-preserved"
190
+ },
191
+ {
192
+ "surface": "render-integration-environment",
193
+ "value": "HWPX_RENDER_QUEUE_URL",
194
+ "classification": "integration-preserved"
195
+ },
196
+ {
197
+ "surface": "render-integration-environment",
198
+ "value": "HWPX_RENDER_QUEUE_SECRET",
199
+ "classification": "integration-preserved"
200
+ },
201
+ {
202
+ "surface": "render-integration-environment",
203
+ "value": "HWPX_RENDER_TRANSPORT_AUTH",
204
+ "classification": "integration-preserved"
205
+ },
206
+ {
207
+ "surface": "render-integration-environment",
208
+ "value": "HWPX_RENDER_CA_FILE",
209
+ "classification": "integration-preserved"
210
+ },
211
+ {
212
+ "surface": "render-integration-environment",
213
+ "value": "HWPX_RENDER_CLIENT_CERT_FILE",
214
+ "classification": "integration-preserved"
215
+ },
216
+ {
217
+ "surface": "render-integration-environment",
218
+ "value": "HWPX_RENDER_CLIENT_KEY_FILE",
219
+ "classification": "integration-preserved"
220
+ },
221
+ {
222
+ "surface": "workflow-security-environment",
223
+ "value": "HWPX_WORKFLOW_ENCRYPTION_KEY",
224
+ "classification": "capability-preserved"
225
+ },
226
+ {
227
+ "surface": "oracle-capability-environment",
228
+ "value": "HWPX_ORACLE_STRUCTURAL_ONLY",
229
+ "classification": "capability-preserved"
230
+ },
231
+ {
232
+ "surface": "oracle-capability-environment",
233
+ "value": "HWPX_ORACLE_BUDGET_SECONDS",
234
+ "classification": "capability-preserved"
235
+ },
236
+ {
237
+ "surface": "plugin-integration-environment",
238
+ "value": "HWPX_SKILL_VERSION",
239
+ "classification": "integration-preserved"
240
+ },
241
+ {
242
+ "surface": "plugin-integration-environment",
243
+ "value": "HWPX_PLUGIN_ROOT",
244
+ "classification": "integration-preserved"
245
+ }
246
+ ],
247
+ "compatibilityPolicy": {
248
+ "supportedThroughMajor": 6,
249
+ "removalNotBeforeMajor": 7,
250
+ "minimumPublicNoticeDays": 90,
251
+ "requiresObservedUsageWindow": true,
252
+ "requiresSeparateOwnerApproval": true,
253
+ "statePathMigrationInMajor6": false
254
+ },
255
+ "notes": {
256
+ "hostConfigKey": "The key is a host-local alias, not the MCP protocol identity.",
257
+ "environmentPrecedence": "HWPX_AUTOMATION_* wins; HWPX_MCP_* remains a 6.x fallback.",
258
+ "preservedWireIdentifiers": "The public hwpx.mcp-error/v1 schema, versions.mcp/minMcpVersion/MIN_MCP_VERSION aliases, frozen hwpx-mcp.* architecture receipt IDs, and mcpRuntimeMembers parity-receipt field remain exact in 6.x. They are compatibility identifiers, not statements that MCP owns the automation product.",
259
+ "preservedIntegrationEnvironment": "Product-neutral HWPX_RENDER_*, HWPX_WORKFLOW_ENCRYPTION_KEY, HWPX_ORACLE_*, HWPX_SKILL_VERSION, and HWPX_PLUGIN_ROOT names do not encode the former MCP product identity. They remain unchanged and are not aliases in the HWPX_AUTOMATION_ transition.",
260
+ "statePath": "HWPX_AUTOMATION_WORKFLOW_STORE wins over the prior HWPX_WORKFLOW_STORE override; the existing hwpx-mcp-server on-disk default remains in 6.x to avoid silent workflow loss.",
261
+ "renderQueuePrincipal": "The signed queue principal remains hwpx-mcp-server in 6.x so queued work and worker authorization do not split during the rename."
262
+ }
263
+ }
@@ -0,0 +1,18 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """Load the installed product-identity contract."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import json
7
+ from importlib.resources import files
8
+ from typing import Any
9
+
10
+
11
+ def product_identity() -> dict[str, Any]:
12
+ """Return a detached copy of the installed identity manifest."""
13
+
14
+ resource = files("hwpx_automation").joinpath("identity.json")
15
+ return json.loads(resource.read_text(encoding="utf-8"))
16
+
17
+
18
+ __all__ = ["product_identity"]
@@ -0,0 +1,85 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """Optional document-ingest adapters for the MCP server layer."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import tempfile
7
+ from pathlib import Path
8
+ from typing import Any, BinaryIO
9
+
10
+ from hwpx.ingest import (
11
+ DocumentIngestError,
12
+ DocumentIngestResult,
13
+ DocumentSourceInfo,
14
+ )
15
+
16
+
17
+ class MissingMarkItDownDependency(DocumentIngestError):
18
+ """Raised when the optional MarkItDown runtime is not installed."""
19
+
20
+
21
+ class MarkItDownAdapter:
22
+ """Fallback converter that delegates non-HWPX sources to Microsoft MarkItDown."""
23
+
24
+ name = "MarkItDownAdapter"
25
+
26
+ def accepts(self, file_stream: BinaryIO, source_info: DocumentSourceInfo) -> bool:
27
+ del file_stream
28
+ extension = (source_info.extension or "").lower()
29
+ return extension != ".hwpx"
30
+
31
+ def convert(
32
+ self,
33
+ file_stream: BinaryIO,
34
+ source_info: DocumentSourceInfo,
35
+ **kwargs: Any,
36
+ ) -> DocumentIngestResult:
37
+ del kwargs
38
+ markitdown_cls = _load_markitdown_class()
39
+ source_path = _source_path(file_stream, source_info)
40
+ result = markitdown_cls().convert(str(source_path))
41
+ markdown = _extract_markdown(result)
42
+ return DocumentIngestResult(
43
+ markdown=markdown,
44
+ source_info=source_info,
45
+ source_format=(source_info.extension or "unknown").lstrip(".") or "unknown",
46
+ engine="markitdown",
47
+ metadata={"converter": self.name},
48
+ warnings=[
49
+ "Converted by optional MarkItDown adapter; layout fidelity is not claimed.",
50
+ ],
51
+ lossiness="unknown",
52
+ )
53
+
54
+
55
+ def _load_markitdown_class() -> type:
56
+ try:
57
+ from markitdown import MarkItDown
58
+ except ImportError as exc:
59
+ raise MissingMarkItDownDependency(
60
+ "install python-hwpx-automation[ingest] (or hwpx-mcp-server[ingest]) "
61
+ "to enable non-HWPX document ingest"
62
+ ) from exc
63
+ return MarkItDown
64
+
65
+
66
+ def _source_path(file_stream: BinaryIO, source_info: DocumentSourceInfo) -> Path:
67
+ if source_info.local_path:
68
+ return Path(source_info.local_path)
69
+
70
+ suffix = source_info.extension or ""
71
+ with tempfile.NamedTemporaryFile(suffix=suffix, delete=False) as tmp:
72
+ start_pos = file_stream.tell()
73
+ tmp.write(file_stream.read())
74
+ file_stream.seek(start_pos)
75
+ return Path(tmp.name)
76
+
77
+
78
+ def _extract_markdown(result: Any) -> str:
79
+ for attr in ("text_content", "markdown", "text"):
80
+ value = getattr(result, attr, None)
81
+ if isinstance(value, str):
82
+ return value
83
+ if isinstance(result, str):
84
+ return result
85
+ raise ValueError("MarkItDown result did not include Markdown text")
@@ -0,0 +1,216 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """Markdown-to-document-plan bridge."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import re
7
+ from dataclasses import dataclass
8
+ from typing import Any, Mapping
9
+
10
+
11
+ @dataclass(frozen=True, slots=True)
12
+ class MarkdownPlanResult:
13
+ plan: dict[str, Any]
14
+ warnings: list[str]
15
+
16
+
17
+ def markdown_to_document_plan(
18
+ markdown: str,
19
+ *,
20
+ title: str | None = None,
21
+ metadata: Mapping[str, Any] | None = None,
22
+ style_preset: str = "standard_korean_business",
23
+ ) -> MarkdownPlanResult:
24
+ """Convert a conservative Markdown subset into ``hwpx.document_plan.v1``."""
25
+
26
+ warnings: list[str] = []
27
+ blocks: list[dict[str, Any]] = []
28
+ lines = (markdown or "").replace("\r\n", "\n").replace("\r", "\n").split("\n")
29
+ resolved_title = (title or "").strip()
30
+ index = 0
31
+
32
+ while index < len(lines):
33
+ line = lines[index]
34
+ stripped = line.strip()
35
+ if not stripped:
36
+ index += 1
37
+ continue
38
+
39
+ if table := _parse_table(lines, index):
40
+ table_block, next_index = table
41
+ blocks.append(table_block)
42
+ index = next_index
43
+ continue
44
+
45
+ heading = _parse_heading(stripped)
46
+ if heading is not None:
47
+ level, text = heading
48
+ if not resolved_title and level == 1 and not blocks:
49
+ resolved_title = text
50
+ else:
51
+ blocks.append({"type": "heading", "level": min(level, 3), "text": text})
52
+ if level > 3:
53
+ warnings.append(f"Heading level {level} was clamped to document_plan level 3.")
54
+ index += 1
55
+ continue
56
+
57
+ if _is_list_item(stripped):
58
+ items, next_index, ordered = _collect_list(lines, index)
59
+ blocks.append({"type": "bullets", "items": items})
60
+ if ordered:
61
+ warnings.append("Ordered Markdown lists were converted to bullet blocks.")
62
+ index = next_index
63
+ continue
64
+
65
+ paragraph, next_index = _collect_paragraph(lines, index)
66
+ if paragraph:
67
+ blocks.append({"type": "paragraph", "text": paragraph})
68
+ index = next_index
69
+
70
+ if not resolved_title:
71
+ resolved_title = _title_from_blocks(blocks) or "Markdown Import"
72
+
73
+ if not blocks:
74
+ blocks.append({"type": "paragraph", "text": "작성 필요"})
75
+ warnings.append("Markdown had no content blocks; inserted an empty paragraph placeholder.")
76
+
77
+ quality_gates = {
78
+ "validatePackage": True,
79
+ "validateDocument": True,
80
+ "reopen": True,
81
+ "minNonEmptyParagraphs": max(1, _non_empty_block_count(blocks)),
82
+ "visualReviewRequired": True,
83
+ }
84
+ return MarkdownPlanResult(
85
+ plan={
86
+ "schemaVersion": "hwpx.document_plan.v1",
87
+ "title": resolved_title,
88
+ "metadata": _string_metadata(metadata or {}),
89
+ "stylePreset": style_preset,
90
+ "blocks": blocks,
91
+ "qualityGates": quality_gates,
92
+ },
93
+ warnings=warnings,
94
+ )
95
+
96
+
97
+ def _parse_heading(stripped: str) -> tuple[int, str] | None:
98
+ match = re.match(r"^(#{1,6})\s+(.+?)\s*#*\s*$", stripped)
99
+ if not match:
100
+ return None
101
+ return len(match.group(1)), match.group(2).strip()
102
+
103
+
104
+ def _is_list_item(stripped: str) -> bool:
105
+ return bool(re.match(r"^([-+*])\s+.+", stripped) or re.match(r"^\d+[.)]\s+.+", stripped))
106
+
107
+
108
+ def _collect_list(lines: list[str], index: int) -> tuple[list[str], int, bool]:
109
+ items: list[str] = []
110
+ ordered = False
111
+ while index < len(lines):
112
+ stripped = lines[index].strip()
113
+ unordered = re.match(r"^[-+*]\s+(.+)$", stripped)
114
+ numbered = re.match(r"^\d+[.)]\s+(.+)$", stripped)
115
+ if unordered:
116
+ items.append(unordered.group(1).strip())
117
+ elif numbered:
118
+ ordered = True
119
+ items.append(numbered.group(1).strip())
120
+ else:
121
+ break
122
+ index += 1
123
+ return items, index, ordered
124
+
125
+
126
+ def _collect_paragraph(lines: list[str], index: int) -> tuple[str, int]:
127
+ parts: list[str] = []
128
+ while index < len(lines):
129
+ stripped = lines[index].strip()
130
+ if not stripped or _parse_heading(stripped) or _is_list_item(stripped) or _parse_table(lines, index):
131
+ break
132
+ parts.append(stripped)
133
+ index += 1
134
+ return " ".join(parts).strip(), index
135
+
136
+
137
+ def _parse_table(lines: list[str], index: int) -> tuple[dict[str, Any], int] | None:
138
+ if index + 1 >= len(lines):
139
+ return None
140
+ header = lines[index].strip()
141
+ divider = lines[index + 1].strip()
142
+ if not _looks_like_table_row(header) or not _looks_like_divider(divider):
143
+ return None
144
+
145
+ labels = _split_table_row(header)
146
+ if not labels:
147
+ return None
148
+ keys = [f"col{col_index + 1}" for col_index in range(len(labels))]
149
+ rows: list[dict[str, str]] = []
150
+ index += 2
151
+ while index < len(lines) and _looks_like_table_row(lines[index].strip()):
152
+ cells = _split_table_row(lines[index].strip())
153
+ row = {
154
+ key: cells[col_index].strip() if col_index < len(cells) else ""
155
+ for col_index, key in enumerate(keys)
156
+ }
157
+ rows.append(row)
158
+ index += 1
159
+
160
+ return {
161
+ "type": "table",
162
+ "columns": [
163
+ {"key": key, "label": label.strip() or key}
164
+ for key, label in zip(keys, labels)
165
+ ],
166
+ "rows": rows,
167
+ }, index
168
+
169
+
170
+ def _looks_like_table_row(line: str) -> bool:
171
+ return "|" in line and len(_split_table_row(line)) >= 2
172
+
173
+
174
+ def _looks_like_divider(line: str) -> bool:
175
+ cells = _split_table_row(line)
176
+ return bool(cells) and all(re.match(r"^:?-{3,}:?$", cell.strip()) for cell in cells)
177
+
178
+
179
+ def _split_table_row(line: str) -> list[str]:
180
+ stripped = line.strip()
181
+ if stripped.startswith("|"):
182
+ stripped = stripped[1:]
183
+ if stripped.endswith("|"):
184
+ stripped = stripped[:-1]
185
+ return [cell.replace(r"\|", "|").strip() for cell in stripped.split("|")]
186
+
187
+
188
+ def _title_from_blocks(blocks: list[dict[str, Any]]) -> str:
189
+ for block in blocks:
190
+ text = str(block.get("text") or "").strip()
191
+ if block.get("type") == "heading" and text:
192
+ return text
193
+ for block in blocks:
194
+ text = str(block.get("text") or "").strip()
195
+ if text:
196
+ return text[:80]
197
+ return ""
198
+
199
+
200
+ def _string_metadata(metadata: Mapping[str, Any]) -> dict[str, str]:
201
+ return {
202
+ str(key): str(value)
203
+ for key, value in metadata.items()
204
+ if value is not None and str(value).strip()
205
+ }
206
+
207
+
208
+ def _non_empty_block_count(blocks: list[dict[str, Any]]) -> int:
209
+ count = 0
210
+ for block in blocks:
211
+ kind = block.get("type")
212
+ if kind in {"heading", "paragraph"} and str(block.get("text") or "").strip():
213
+ count += 1
214
+ elif kind == "bullets":
215
+ count += len(block.get("items") or [])
216
+ return count
@@ -0,0 +1,29 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """Guarded entry point for the optional MCP adapter."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import sys
7
+ from collections.abc import Sequence
8
+
9
+ _INSTALL_HINT = (
10
+ 'MCP support is optional. Install it with '
11
+ '"python-hwpx-automation[mcp]" and retry.'
12
+ )
13
+
14
+
15
+ def main(argv: Sequence[str] | None = None) -> None:
16
+ """Start the MCP adapter or fail with an actionable extra-install hint."""
17
+
18
+ try:
19
+ from .server import main as server_main
20
+ except ModuleNotFoundError as exc:
21
+ if exc.name == "mcp" or (exc.name or "").startswith("mcp."):
22
+ print(_INSTALL_HINT, file=sys.stderr)
23
+ raise SystemExit(2) from exc
24
+ raise
25
+ server_main(list(argv) if argv is not None else None)
26
+
27
+
28
+ if __name__ == "__main__":
29
+ main()
@@ -0,0 +1,40 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """Static metadata used for hardened tool responses."""
3
+
4
+ from __future__ import annotations
5
+
6
+ from dataclasses import dataclass
7
+ from typing import Dict
8
+
9
+
10
+ @dataclass(frozen=True)
11
+ class ExampleTemplate:
12
+ tool: str
13
+ example_json: str
14
+
15
+ def render(self, plan_id: str) -> Dict[str, str]:
16
+ return {
17
+ "tool": self.tool,
18
+ "exampleJson": self.example_json.replace("<plan-id>", plan_id),
19
+ }
20
+
21
+
22
+ PLAN_NEXT_ACTION = ExampleTemplate(
23
+ tool="hwpx.preview_edit",
24
+ example_json='{"planId":"<plan-id>"}',
25
+ )
26
+
27
+ PREVIEW_NEXT_ACTION = ExampleTemplate(
28
+ tool="hwpx.apply_edit",
29
+ example_json='{"planId":"<plan-id>","confirm":true}',
30
+ )
31
+
32
+ APPLY_BAD_EXAMPLE = ExampleTemplate(
33
+ tool="hwpx.apply_edit",
34
+ example_json='{"planId":"<plan-id>"}',
35
+ )
36
+
37
+ ERROR_PREVIEW_REQUIRED = ExampleTemplate(
38
+ tool="hwpx.preview_edit",
39
+ example_json='{"planId":"<plan-id>"}',
40
+ )