python-hwpx-automation 7.0.4__tar.gz → 7.2.0__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.
Files changed (233) hide show
  1. {python_hwpx_automation-7.0.4/src/python_hwpx_automation.egg-info → python_hwpx_automation-7.2.0}/PKG-INFO +7 -7
  2. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/README.md +5 -5
  3. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/pyproject.toml +2 -2
  4. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/handlers/content_edit.py +48 -3
  5. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/handlers/read_export.py +3 -1
  6. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/identity.json +18 -10
  7. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/mixed_form.py +3 -3
  8. python_hwpx_automation-7.2.0/src/hwpx_automation/office/agent/_batch_publication.py +185 -0
  9. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/agent/_batch_verification.py +10 -4
  10. python_hwpx_automation-7.2.0/src/hwpx_automation/office/agent/_text_scope.py +379 -0
  11. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/agent/blueprint/dump.py +62 -6
  12. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/agent/cli.py +20 -9
  13. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/agent/commands.py +16 -2
  14. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/agent/document.py +30 -2
  15. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/__init__.py +61 -38
  16. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/rendering/_render_hwpx_mac.applescript +47 -35
  17. python_hwpx_automation-7.2.0/src/hwpx_automation/office/rendering/mac_session.py +87 -0
  18. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/rendering/oracle.py +33 -42
  19. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/rendering/worker.py +55 -17
  20. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/ops_services/content_layout.py +1 -1
  21. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/ops_services/memo_style.py +1 -1
  22. python_hwpx_automation-7.2.0/src/hwpx_automation/public-modules.json +215 -0
  23. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/runtime.py +28 -4
  24. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/tool_contract.py +5 -4
  25. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/upstream.py +14 -3
  26. python_hwpx_automation-7.2.0/src/hwpx_automation/utils/read_budget.py +128 -0
  27. python_hwpx_automation-7.2.0/src/hwpx_automation/workflow/evidence_lineage.py +131 -0
  28. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/workflow/render_queue.py +17 -0
  29. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/workflow/service.py +14 -4
  30. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0/src/python_hwpx_automation.egg-info}/PKG-INFO +7 -7
  31. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/python_hwpx_automation.egg-info/SOURCES.txt +5 -0
  32. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/python_hwpx_automation.egg-info/requires.txt +1 -1
  33. python_hwpx_automation-7.0.4/src/hwpx_automation/public-modules.json +0 -210
  34. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/LICENSE +0 -0
  35. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/MANIFEST.in +0 -0
  36. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/NOTICE +0 -0
  37. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/setup.cfg +0 -0
  38. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/__init__.py +0 -0
  39. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/__init__.pyi +0 -0
  40. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/__main__.py +0 -0
  41. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/agent_document.py +0 -0
  42. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/api.py +0 -0
  43. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/blind_eval.py +0 -0
  44. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/capabilities.py +0 -0
  45. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/compat.py +0 -0
  46. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/configuration.py +0 -0
  47. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/core/__init__.py +0 -0
  48. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/core/content.py +0 -0
  49. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/core/context.py +0 -0
  50. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/core/diff.py +0 -0
  51. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/core/document.py +0 -0
  52. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/core/formatting.py +0 -0
  53. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/core/handles.py +0 -0
  54. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/core/locations.py +0 -0
  55. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/core/locator.py +0 -0
  56. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/core/plan.py +0 -0
  57. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/core/resources.py +0 -0
  58. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/core/search.py +0 -0
  59. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/core/transactions.py +0 -0
  60. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/core/txn.py +0 -0
  61. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/document_state.py +0 -0
  62. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/errors.py +0 -0
  63. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/execution_lock.py +0 -0
  64. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/fastmcp_adapter.py +0 -0
  65. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/form_fill.py +0 -0
  66. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/form_output_models.py +0 -0
  67. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/handlers/__init__.py +0 -0
  68. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/handlers/_shared.py +0 -0
  69. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/handlers/agent_document.py +0 -0
  70. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/handlers/authoring.py +0 -0
  71. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/handlers/form_fill.py +0 -0
  72. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/handlers/layout_style.py +0 -0
  73. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/handlers/quality_render.py +0 -0
  74. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/handlers/specialized.py +0 -0
  75. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/handlers/tracked_changes.py +0 -0
  76. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/handlers/workflow.py +0 -0
  77. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/hwp_converter.py +0 -0
  78. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/hwp_support.py +0 -0
  79. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/hwpx_ops.py +0 -0
  80. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/identity.py +0 -0
  81. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/ingest_adapters.py +0 -0
  82. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/markdown_plan.py +0 -0
  83. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/mcp_cli.py +0 -0
  84. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/metadata/tools_meta.py +0 -0
  85. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/mutation_models.py +0 -0
  86. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/network_policy.py +0 -0
  87. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/__init__.py +0 -0
  88. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/agent/__init__.py +0 -0
  89. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/agent/blueprint/__init__.py +0 -0
  90. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/agent/blueprint/bundle.py +0 -0
  91. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/agent/blueprint/catalog.py +0 -0
  92. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/agent/blueprint/mapping.py +0 -0
  93. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/agent/blueprint/model.py +0 -0
  94. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/agent/blueprint/native.py +0 -0
  95. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/agent/blueprint/replay.py +0 -0
  96. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/agent/catalog.py +0 -0
  97. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/agent/form_plan.py +0 -0
  98. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/agent/model.py +0 -0
  99. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/agent/path.py +0 -0
  100. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/agent/query.py +0 -0
  101. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/agent/story.py +0 -0
  102. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/advanced_generators.py +0 -0
  103. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/builder/__init__.py +0 -0
  104. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/builder/core.py +0 -0
  105. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/builder/report.py +0 -0
  106. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/__init__.py +0 -0
  107. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/_support.py +0 -0
  108. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/composer.py +0 -0
  109. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/harvest.py +0 -0
  110. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/plan.py +0 -0
  111. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/profile.py +0 -0
  112. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/profiles/application_form/fragments/body.xml +0 -0
  113. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/profiles/application_form/fragments/heading.xml +0 -0
  114. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/profiles/application_form/fragments/info_table.xml +0 -0
  115. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/profiles/application_form/fragments/title.xml +0 -0
  116. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/profiles/application_form/profile.json +0 -0
  117. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/profiles/application_form/template.hwpx +0 -0
  118. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/profiles/home_notice/fragments/body.xml +0 -0
  119. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/profiles/home_notice/fragments/heading.xml +0 -0
  120. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/profiles/home_notice/fragments/title.xml +0 -0
  121. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/profiles/home_notice/profile.json +0 -0
  122. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/profiles/home_notice/template.hwpx +0 -0
  123. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/profiles/official_notice/fragments/body.xml +0 -0
  124. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/profiles/official_notice/fragments/heading.xml +0 -0
  125. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/profiles/official_notice/fragments/info_table.xml +0 -0
  126. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/profiles/official_notice/fragments/title.xml +0 -0
  127. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/profiles/official_notice/profile.json +0 -0
  128. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/profiles/official_notice/template.hwpx +0 -0
  129. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/profiles/report/fragments/body.xml +0 -0
  130. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/profiles/report/fragments/heading.xml +0 -0
  131. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/profiles/report/fragments/info_table.xml +0 -0
  132. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/profiles/report/fragments/title.xml +0 -0
  133. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/profiles/report/profile.json +0 -0
  134. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/profiles/report/template.hwpx +0 -0
  135. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/design/validator.py +0 -0
  136. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/presets/__init__.py +0 -0
  137. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/presets/proposal.py +0 -0
  138. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/report_parser.py +0 -0
  139. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/style_profile.py +0 -0
  140. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/authoring/template_analyzer.py +0 -0
  141. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/charting/__init__.py +0 -0
  142. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/compliance/__init__.py +0 -0
  143. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/compliance/official_lint.py +0 -0
  144. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/compliance/pii.py +0 -0
  145. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/document_ops/__init__.py +0 -0
  146. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/document_ops/comparison.py +0 -0
  147. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/document_ops/mail_merge.py +0 -0
  148. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/document_ops/redline.py +0 -0
  149. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/evalplan/__init__.py +0 -0
  150. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/evalplan/runtime.py +0 -0
  151. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/exam/__init__.py +0 -0
  152. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/exam/compose.py +0 -0
  153. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/exam/ir.py +0 -0
  154. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/exam/measure.py +0 -0
  155. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/exam/parser.py +0 -0
  156. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/exam/profile.py +0 -0
  157. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/form_fill/__init__.py +0 -0
  158. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/form_fill/classification.py +0 -0
  159. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/form_fill/fill_residue.py +0 -0
  160. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/form_fill/fit/__init__.py +0 -0
  161. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/form_fill/fit/apply.py +0 -0
  162. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/form_fill/fit/engine.py +0 -0
  163. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/form_fill/fit/measure.py +0 -0
  164. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/form_fill/fit/policy.py +0 -0
  165. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/form_fill/fit/report.py +0 -0
  166. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/form_fill/fit/seal.py +0 -0
  167. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/form_fill/fit/wordbox.py +0 -0
  168. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/form_fill/guidance.py +0 -0
  169. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/form_fill/quality.py +0 -0
  170. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/form_fill/split_run.py +0 -0
  171. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/form_fill/template_formfit.py +0 -0
  172. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/house_style/__init__.py +0 -0
  173. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/house_style/composition.py +0 -0
  174. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/house_style/data/bank.json +0 -0
  175. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/house_style/data/genres.json +0 -0
  176. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/house_style/gianmun.py +0 -0
  177. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/house_style/org_chart.py +0 -0
  178. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/quality/__init__.py +0 -0
  179. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/quality/page_guard.py +0 -0
  180. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/rendering/__init__.py +0 -0
  181. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/rendering/_hancom_open_rate.ps1 +0 -0
  182. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/rendering/_refresh_hwpx_mac.applescript +0 -0
  183. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/rendering/_render_hwpx.ps1 +0 -0
  184. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/rendering/block_splits.py +0 -0
  185. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/rendering/detectors.py +0 -0
  186. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/rendering/diff.py +0 -0
  187. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/rendering/fixture_corpus.py +0 -0
  188. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/rendering/page_qa.py +0 -0
  189. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/rendering/qa_contracts.py +0 -0
  190. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/rendering/qa_metrics.py +0 -0
  191. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/utilities/__init__.py +0 -0
  192. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/office/utilities/table_compute.py +0 -0
  193. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/ops_services/__init__.py +0 -0
  194. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/ops_services/_border_fill.py +0 -0
  195. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/ops_services/composition.py +0 -0
  196. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/ops_services/context.py +0 -0
  197. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/ops_services/form_fields.py +0 -0
  198. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/ops_services/media.py +0 -0
  199. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/ops_services/package_validation.py +0 -0
  200. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/ops_services/planning.py +0 -0
  201. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/ops_services/preview_export.py +0 -0
  202. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/ops_services/read_query.py +0 -0
  203. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/ops_services/save_policy.py +0 -0
  204. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/ops_services/tables.py +0 -0
  205. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/ops_services/transactions.py +0 -0
  206. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/preview_output_models.py +0 -0
  207. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/py.typed +0 -0
  208. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/quality.py +0 -0
  209. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/quality_generation.py +0 -0
  210. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/resources.py +0 -0
  211. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/runtime_services.py +0 -0
  212. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/server.py +0 -0
  213. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/storage.py +0 -0
  214. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/tool_bindings.py +0 -0
  215. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/utils/__init__.py +0 -0
  216. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/utils/helpers.py +0 -0
  217. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/visual_qa.py +0 -0
  218. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/workflow/__init__.py +0 -0
  219. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/workflow/adapters.py +0 -0
  220. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/workflow/dispatcher.py +0 -0
  221. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/workflow/models.py +0 -0
  222. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/workflow/policy.py +0 -0
  223. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/workflow/render_contracts.py +0 -0
  224. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/workflow/render_metrics.py +0 -0
  225. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/workflow/render_security.py +0 -0
  226. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/workflow/render_transport.py +0 -0
  227. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/workflow/rendering.py +0 -0
  228. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/workflow/state_machine.py +0 -0
  229. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/workflow/store.py +0 -0
  230. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/hwpx_automation/workspace.py +0 -0
  231. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/python_hwpx_automation.egg-info/dependency_links.txt +0 -0
  232. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/python_hwpx_automation.egg-info/entry_points.txt +0 -0
  233. {python_hwpx_automation-7.0.4 → python_hwpx_automation-7.2.0}/src/python_hwpx_automation.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-hwpx-automation
3
- Version: 7.0.4
3
+ Version: 7.2.0
4
4
  Summary: Task-oriented HWPX document automation for Python, with an optional MCP adapter.
5
5
  Author: Kohkyuhyun
6
6
  License-Expression: Apache-2.0
@@ -20,7 +20,7 @@ Requires-Python: >=3.10
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
22
  License-File: NOTICE
23
- Requires-Dist: python-hwpx<7,>=6.3.0
23
+ Requires-Dist: python-hwpx<7,>=6.5.0
24
24
  Requires-Dist: pydantic<3,>=2.11
25
25
  Requires-Dist: cryptography>=42.0
26
26
  Requires-Dist: anyio>=4.0
@@ -74,10 +74,8 @@ Dynamic: license-file
74
74
 
75
75
  <!-- release-state: release-approved -->
76
76
  > [!NOTE]
77
- > 아직 공개되지 않은 7.0.4 후보입니다.
78
- > 후보 조합: `python-hwpx 6.3.0 → python-hwpx-automation 7.0.4 → hwpx-plugin 2.1.0`.
79
- > 공개 트레인: `python-hwpx 6.3.0 → python-hwpx-automation 7.0.3 → hwpx-plugin 2.0.3`.
80
- > 계약은 `8c278ebd5becba08`이며 도구 표면과 최소 호환 버전은 유지합니다.
77
+ > 발행이 승인된 7.2.0 후보 (release-approved; 원격 발행 대기): `python-hwpx 6.5.0 → python-hwpx-automation 7.2.0 → hwpx-plugin 2.3.0` (`5e5c23651f92785a`).
78
+ > 공개 트레인: `python-hwpx 6.4.0 → python-hwpx-automation 7.1.0 → hwpx-plugin 2.2.0` (`ba0211fc854a0a97`).
81
79
  > 공개 좌표는 core·automation PyPI와 plugin GitHub
82
80
  > Release·marketplace·실제 marketplace 설치 관찰 후에만 승격합니다.
83
81
  > [릴리스 runbook](docs/release-runbook.md)
@@ -134,7 +132,7 @@ Gemini CLI는 `~/.gemini/settings.json`, Cursor·Windsurf는 각 에디터의 MC
134
132
  "command": "uvx",
135
133
  "args": [
136
134
  "--from",
137
- "python-hwpx-automation[mcp]==7.0.3",
135
+ "python-hwpx-automation[mcp]==7.1.0",
138
136
  "hwpx-automation-mcp"
139
137
  ],
140
138
  "env": {
@@ -241,3 +239,5 @@ python scripts/run_conformance.py run \
241
239
  ## License · Maintainer
242
240
 
243
241
  Apache-2.0 ([LICENSE](LICENSE) · [NOTICE](NOTICE)) — **Kohkyuhyun** [@airmang](https://github.com/airmang) · [kokyuhyun@hotmail.com](mailto:kokyuhyun@hotmail.com)
242
+
243
+ 기존 문서 수정은 [조회·대상 확정·보존 저장·검증 안내](docs/existing-edit-navigation.md)를 먼저 확인하세요. 새 문서는 document-plan, 에이전트 연결은 선택 MCP와 호스트 플러그인을 사용합니다.
@@ -14,10 +14,8 @@
14
14
 
15
15
  <!-- release-state: release-approved -->
16
16
  > [!NOTE]
17
- > 아직 공개되지 않은 7.0.4 후보입니다.
18
- > 후보 조합: `python-hwpx 6.3.0 → python-hwpx-automation 7.0.4 → hwpx-plugin 2.1.0`.
19
- > 공개 트레인: `python-hwpx 6.3.0 → python-hwpx-automation 7.0.3 → hwpx-plugin 2.0.3`.
20
- > 계약은 `8c278ebd5becba08`이며 도구 표면과 최소 호환 버전은 유지합니다.
17
+ > 발행이 승인된 7.2.0 후보 (release-approved; 원격 발행 대기): `python-hwpx 6.5.0 → python-hwpx-automation 7.2.0 → hwpx-plugin 2.3.0` (`5e5c23651f92785a`).
18
+ > 공개 트레인: `python-hwpx 6.4.0 → python-hwpx-automation 7.1.0 → hwpx-plugin 2.2.0` (`ba0211fc854a0a97`).
21
19
  > 공개 좌표는 core·automation PyPI와 plugin GitHub
22
20
  > Release·marketplace·실제 marketplace 설치 관찰 후에만 승격합니다.
23
21
  > [릴리스 runbook](docs/release-runbook.md)
@@ -74,7 +72,7 @@ Gemini CLI는 `~/.gemini/settings.json`, Cursor·Windsurf는 각 에디터의 MC
74
72
  "command": "uvx",
75
73
  "args": [
76
74
  "--from",
77
- "python-hwpx-automation[mcp]==7.0.3",
75
+ "python-hwpx-automation[mcp]==7.1.0",
78
76
  "hwpx-automation-mcp"
79
77
  ],
80
78
  "env": {
@@ -181,3 +179,5 @@ python scripts/run_conformance.py run \
181
179
  ## License · Maintainer
182
180
 
183
181
  Apache-2.0 ([LICENSE](LICENSE) · [NOTICE](NOTICE)) — **Kohkyuhyun** [@airmang](https://github.com/airmang) · [kokyuhyun@hotmail.com](mailto:kokyuhyun@hotmail.com)
182
+
183
+ 기존 문서 수정은 [조회·대상 확정·보존 저장·검증 안내](docs/existing-edit-navigation.md)를 먼저 확인하세요. 새 문서는 document-plan, 에이전트 연결은 선택 MCP와 호스트 플러그인을 사용합니다.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-hwpx-automation"
7
- version = "7.0.4"
7
+ version = "7.2.0"
8
8
  description = "Task-oriented HWPX document automation for Python, with an optional MCP adapter."
9
9
  readme = "README.md"
10
10
  authors = [{ name = "Kohkyuhyun" }]
@@ -31,7 +31,7 @@ dependencies = [
31
31
  # breaking an already-published automation line. WP-F (engine surface 6.0):
32
32
  # this line adapted to the new namespace surface (design §9 WP-F) — the dev0
33
33
  # pre-release qualifier tracks core's own not-yet-tagged 6.0 identity.
34
- "python-hwpx>=6.3.0,<7", # HWPX object model, OXML/OPC, format-native primitives
34
+ "python-hwpx>=6.5.0,<7", # HWPX object model, OXML/OPC, format-native primitives
35
35
 
36
36
  "pydantic>=2.11,<3",
37
37
  "cryptography>=42.0",
@@ -3,7 +3,9 @@
3
3
 
4
4
  from __future__ import annotations
5
5
 
6
+ from pathlib import Path
6
7
  from typing import Any
8
+ import zlib
7
9
 
8
10
  from .. import quality as quality_contract
9
11
  from ..core.content import (
@@ -805,9 +807,40 @@ def insert_picture(
805
807
  return _with_save_verification(result, verification)
806
808
 
807
809
 
810
+ def _validate_workspace_image(data: bytes, image_format: str) -> None:
811
+ """Check bounded file input before inserting its bytes into a package."""
812
+ fmt = image_format.casefold().lstrip(".")
813
+ if fmt == "png":
814
+ if not data.startswith(b"\x89PNG\r\n\x1a\n"):
815
+ raise ValueError("image_filename content does not match image_format")
816
+ offset = 8
817
+ chunks: list[bytes] = []
818
+ while offset + 12 <= len(data):
819
+ length = int.from_bytes(data[offset:offset + 4], "big")
820
+ end = offset + 12 + length
821
+ if end > len(data):
822
+ break
823
+ kind = data[offset + 4:offset + 8]
824
+ payload = data[offset + 8:offset + 8 + length]
825
+ crc = int.from_bytes(data[end - 4:end], "big")
826
+ if zlib.crc32(kind + payload) != crc:
827
+ break
828
+ chunks.append(kind)
829
+ offset = end
830
+ if kind == b"IEND":
831
+ if length == 0 and offset == len(data) and chunks[0] == b"IHDR" and b"IDAT" in chunks:
832
+ return
833
+ break
834
+ elif fmt in {"jpg", "jpeg"} and data.startswith(b"\xff\xd8\xff") and data.endswith(b"\xff\xd9"):
835
+ return
836
+ elif fmt == "gif" and data.startswith((b"GIF87a", b"GIF89a")) and data.endswith(b";"):
837
+ return
838
+ raise ValueError("image_filename content does not match image_format or is incomplete")
839
+
840
+
808
841
  def replace_picture(
809
842
  filename: str,
810
- image_base64: str,
843
+ image_base64: str | None = None,
811
844
  image_format: str = "png",
812
845
  picture_index: int = 0,
813
846
  binary_item_id_ref: str | None = None,
@@ -815,15 +848,27 @@ def replace_picture(
815
848
  output: str | None = None,
816
849
  dry_run: bool = False,
817
850
  expected_revision: str = None,
851
+ image_filename: str | None = None,
818
852
  ) -> dict:
819
- """그림 객체의 geometry를 유지하고 연결된 이미지 asset만 교체합니다."""
853
+ """그림 geometry를 유지하고 base64 또는 workspace 이미지 파일로 교체합니다."""
854
+ if (image_base64 is None) == (image_filename is None):
855
+ raise ValueError("provide exactly one of image_base64 or image_filename")
820
856
  path = resolve_path(filename)
821
857
  guard = _revision_guard(path, expected_revision)
822
858
  if guard is not None:
823
859
  return guard
824
860
  target_path = resolve_path(output) if output else path
861
+ if image_filename is not None:
862
+ image_path = Path(resolve_path(image_filename))
863
+ with image_path.open("rb") as image_file:
864
+ image_data = image_file.read(20 * 1024 * 1024 + 1)
865
+ if len(image_data) > 20 * 1024 * 1024:
866
+ raise ValueError("image_filename must be at most 20 MiB")
867
+ _validate_workspace_image(image_data, image_format)
868
+ else:
869
+ assert image_base64 is not None
870
+ image_data = _decode_image_base64(image_base64)
825
871
  doc = open_doc(path)
826
- image_data = _decode_image_base64(image_base64)
827
872
  # media.replace_picture now returns a frozen PictureReplacement (design
828
873
  # §2.4) whose own .to_dict() uses different field names than this op's
829
874
  # established response contract (old_binaryItemIDRef/new_binaryItemIDRef/
@@ -38,6 +38,7 @@ from ..upstream import (
38
38
  open_document,
39
39
  )
40
40
  from ..utils.helpers import default_max_chars, resolve_path, truncate_response
41
+ from ..utils.read_budget import bound_document_summary
41
42
  from ._shared import _with_document_state
42
43
 
43
44
  _OUTPUT_MODES = {"full", "chunks"}
@@ -920,7 +921,8 @@ def get_document_map(
920
921
  "hwpx_extract_json",
921
922
  ],
922
923
  }
923
- return _with_document_state(result, path)
924
+ result = _with_document_state(result, path)
925
+ return bound_document_summary(result) if summary_mode else result
924
926
 
925
927
 
926
928
  def hwpx_to_markdown(
@@ -5,22 +5,30 @@
5
5
  "releaseState": {
6
6
  "status": "release-approved",
7
7
  "candidate": {
8
- "pythonHwpx": "6.3.0",
8
+ "pythonHwpx": "6.5.0",
9
9
  "canonicalDistribution": "python-hwpx-automation",
10
- "canonicalAutomation": "7.0.4",
10
+ "canonicalAutomation": "7.2.0",
11
11
  "compatibilityDistribution": "hwpx-mcp-server",
12
- "compatibility": "7.0.4",
13
- "plugin": "2.1.0",
14
- "contractHash": "8c278ebd5becba08"
12
+ "compatibility": "7.2.0",
13
+ "plugin": "2.3.0",
14
+ "contractHash": "5e5c23651f92785a"
15
15
  },
16
16
  "currentPublic": {
17
- "pythonHwpx": "6.3.0",
17
+ "pythonHwpx": "6.4.0",
18
18
  "primaryDistribution": "python-hwpx-automation",
19
- "primaryApplication": "7.0.3",
20
- "plugin": "2.0.3",
21
- "contractHash": "8c278ebd5becba08"
19
+ "primaryApplication": "7.1.0",
20
+ "plugin": "2.2.0",
21
+ "contractHash": "ba0211fc854a0a97"
22
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."
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
+ "previousPublic": {
25
+ "pythonHwpx": "6.4.0",
26
+ "primaryDistribution": "python-hwpx-automation",
27
+ "primaryApplication": "7.1.0",
28
+ "plugin": "2.2.0",
29
+ "contractHash": "ba0211fc854a0a97"
30
+ },
31
+ "publicationEvidence": null
24
32
  },
25
33
  "releaseFloors": {
26
34
  "description": "Frozen historical coordinates. Unlike releaseState these do not advance train by train; they pin the pre-6.0 legacy compatibility cap that every core 5 resolution must be observed against. They live here so the release path has no version literal that is not declared data.",
@@ -29,7 +29,6 @@ from hwpx_automation.office.agent import (
29
29
  plan_mixed_form_fill,
30
30
  validate_mixed_form_plan,
31
31
  )
32
- from hwpx.quality import SavePipeline
33
32
  from pydantic import (
34
33
  BaseModel,
35
34
  ConfigDict,
@@ -40,6 +39,7 @@ from pydantic import (
40
39
  )
41
40
 
42
41
  from .execution_lock import PUBLIC_MUTATION_LOCK
42
+ from .office.agent._batch_publication import GuardedSavePipeline
43
43
  from .office.rendering import resolve_hancom_backend
44
44
  from .storage import build_hwpx_open_safety_report
45
45
  from .utils.helpers import resolve_path
@@ -584,7 +584,7 @@ class _FailurePreimagePreserver:
584
584
  return True
585
585
 
586
586
 
587
- class _WorkspaceSavePipeline(SavePipeline):
587
+ class _WorkspaceSavePipeline(GuardedSavePipeline):
588
588
  """Run the core quality gate but publish through an identity-bound path."""
589
589
 
590
590
  def __init__(
@@ -1652,7 +1652,7 @@ def _finalize_canonical_apply(
1652
1652
  verification = copy.deepcopy(core_verification)
1653
1653
  response_ok = bool(response.get("ok")) and bool(source_report["ok"])
1654
1654
  missing_fresh_publication = bool(
1655
- response_ok
1655
+ (response_ok or core_verification.get("publication", {}).get("ownershipMissing"))
1656
1656
  and not dry_run
1657
1657
  and not replayed
1658
1658
  and publication is None
@@ -0,0 +1,185 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """Revision-bound publication for the existing agent command executor.
3
+
4
+ Quality remains in core SavePipeline; identity/CAS and rollback reuse the
5
+ application workspace writer. Explicit Python paths do not acquire a cwd policy.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from pathlib import Path
11
+ from typing import Any
12
+
13
+ from hwpx.quality import SavePipeline
14
+
15
+ from ...workspace import WorkspaceOutputGuard, WorkspaceResolver
16
+ from .model import AgentContractError
17
+
18
+
19
+ class GuardedSavePipeline(SavePipeline):
20
+ """Application publisher carrying the identity of its own publication.
21
+
22
+ Existing domain publishers subclass this seam so the batch can retain their
23
+ guarded publication and recovery receipts after running their quality gate.
24
+ """
25
+
26
+ publication: WorkspaceOutputGuard | None = None
27
+
28
+
29
+ class _BatchSavePipeline(GuardedSavePipeline):
30
+ def __init__(self, binding: BatchPublication) -> None:
31
+ super().__init__()
32
+ self.binding = binding
33
+ self.publication = None
34
+
35
+ def _publish(self, data: bytes, output_path: Any, output_stream: Any) -> str:
36
+ if (
37
+ output_stream is not None
38
+ or Path(output_path).resolve() != self.binding.output
39
+ ):
40
+ raise AgentContractError(
41
+ "verification_failed", "unexpected batch publication destination"
42
+ )
43
+ guard = self.binding.workspace.materialize_output_guard(
44
+ self.binding.output_guard
45
+ )
46
+ try:
47
+ self.publication = self.binding.workspace.atomic_publish_bytes(guard, data)
48
+ except BaseException:
49
+ self.binding.workspace.cleanup_owned_parent_directories(guard)
50
+ raise
51
+ return str(self.publication.path)
52
+
53
+
54
+ def _existing_parent(path: Path) -> Path:
55
+ parent = path.parent
56
+ while not parent.exists() and parent != parent.parent:
57
+ parent = parent.parent
58
+ return parent
59
+
60
+
61
+ class BatchPublication:
62
+ """One disposable request's input snapshot and destination precondition."""
63
+
64
+ def __init__(self, source: Path, output: Path) -> None:
65
+ self.source_argument = source.absolute()
66
+ self.source = source.resolve()
67
+ self.output = output.resolve()
68
+ self.workspace = WorkspaceResolver.from_roots(
69
+ [self.source.parent, _existing_parent(self.output)],
70
+ source="agent-explicit-paths",
71
+ )
72
+ self.source_guard = self.workspace.capture_output(self.source)
73
+ self.input_data = self.workspace.read_guarded_bytes(self.source_guard)
74
+ self.output_guard = self.workspace.capture_output_precondition(self.output)
75
+ self.output_data = (
76
+ self.workspace.read_guarded_bytes(self.output_guard)
77
+ if isinstance(self.output_guard, WorkspaceOutputGuard)
78
+ and self.output_guard.target_existed
79
+ else None
80
+ )
81
+ self.publisher: GuardedSavePipeline | None = None
82
+ self.publication_attempted = False
83
+
84
+ def assert_current_source(self) -> None:
85
+ try:
86
+ if self.source_argument.resolve() != self.source:
87
+ raise OSError("source path was rebound")
88
+ current = self.workspace.read_guarded_bytes(self.source_guard)
89
+ if current != self.input_data:
90
+ raise OSError("source bytes changed")
91
+ except OSError as exc:
92
+ raise AgentContractError(
93
+ "stale_revision",
94
+ "input changed during editing; read its current revision before retrying",
95
+ target="batch.input.filename",
96
+ ) from exc
97
+
98
+ def publish(
99
+ self, data: bytes, pipeline: SavePipeline, verification: dict[str, Any]
100
+ ) -> str:
101
+ self.assert_current_source()
102
+ if not isinstance(pipeline, GuardedSavePipeline) and type(pipeline)._publish is not SavePipeline._publish:
103
+ raise AgentContractError(
104
+ "unsupported_operation",
105
+ "custom publishers must provide an identity-bound publication receipt",
106
+ target="savePipeline",
107
+ )
108
+ self.publisher = (
109
+ pipeline
110
+ if isinstance(pipeline, GuardedSavePipeline)
111
+ else _BatchSavePipeline(self)
112
+ )
113
+ self.publisher.publication = None
114
+ self.publication_attempted = True
115
+ written = self.publisher._publish(data, self.output, None)
116
+ publication = self.publisher.publication
117
+ if publication is None:
118
+ verification["publication"] = {
119
+ "ok": False,
120
+ "ownershipMissing": True,
121
+ "rolledBack": False,
122
+ }
123
+ raise AgentContractError(
124
+ "verification_failed",
125
+ "publisher did not provide an owned output receipt",
126
+ )
127
+ if self.source != self.output:
128
+ self.assert_current_source()
129
+ if self.workspace.read_guarded_bytes(publication) != data:
130
+ raise AgentContractError(
131
+ "verification_failed",
132
+ "published bytes differ from the verified candidate",
133
+ )
134
+ verification["publication"] = {
135
+ "ok": True,
136
+ "inputRechecked": True,
137
+ "outputPreconditionChecked": True,
138
+ }
139
+ return str(written)
140
+
141
+ def rollback(self, verification: dict[str, Any]) -> None:
142
+ publication = self.publisher.publication if self.publisher is not None else None
143
+ if publication is None:
144
+ unchanged = not self.publication_attempted
145
+ if not unchanged:
146
+ try:
147
+ unchanged = (
148
+ self.workspace.capture_output_precondition(self.output)
149
+ == self.output_guard
150
+ )
151
+ except OSError:
152
+ unchanged = False
153
+ verification.setdefault(
154
+ "publication",
155
+ {
156
+ "ok": False,
157
+ "candidatePublished": None if self.publication_attempted else False,
158
+ "rolledBack": unchanged,
159
+ },
160
+ )
161
+ return
162
+ restored = False
163
+ try:
164
+ if self.output_data is None:
165
+ self.workspace.remove_output(publication)
166
+ restored = self.workspace.cleanup_owned_parent_directories(publication)
167
+ else:
168
+ mode = (
169
+ self.output_guard.target_mode
170
+ if isinstance(self.output_guard, WorkspaceOutputGuard)
171
+ else None
172
+ )
173
+ self.workspace.atomic_publish_bytes(
174
+ publication, self.output_data, mode=mode
175
+ )
176
+ restored = True
177
+ except (OSError, RuntimeError):
178
+ # The publication capability never authorizes replacing another
179
+ # writer's newer file. Do not claim that this request rolled it back.
180
+ pass
181
+ verification["publication"] = {
182
+ "ok": False,
183
+ "candidatePublished": True,
184
+ "rolledBack": restored,
185
+ }
@@ -22,6 +22,7 @@ from hwpx.mutation_report import member_diff_bytes
22
22
  from hwpx.quality import QualityPolicy, SavePipeline
23
23
  from hwpx.tools.package_validator import validate_editor_open_safety
24
24
 
25
+ from ._batch_publication import BatchPublication
25
26
  from .document import HwpxAgentDocument
26
27
  from .model import AgentBatchResult, AgentContractError, AgentError
27
28
 
@@ -82,7 +83,7 @@ def _failure_result(
82
83
  output_filename = str(output.get("filename") or "")
83
84
  return AgentBatchResult(
84
85
  ok=False,
85
- rolled_back=True,
86
+ rolled_back=bool((verification or {}).get("publication", {}).get("rolledBack", True)),
86
87
  dry_run=bool(raw.get("dryRun", False)),
87
88
  input_revision=input_revision,
88
89
  document_revision=input_revision,
@@ -291,7 +292,7 @@ def _apply_commands_build_candidate_report(
291
292
  "package": safety_dict["validatePackage"],
292
293
  "reopen": safety_dict["reopen"],
293
294
  "openSafety": safety_dict,
294
- "semanticDiff": {"ok": True, "changeCount": len(semantic_changes)},
295
+ "semanticDiff": {"ok": None, "changeCount": len(semantic_changes), "basis": "declared-command-log"},
295
296
  "bytePreservation": byte_report,
296
297
  }
297
298
  )
@@ -343,6 +344,7 @@ def _apply_commands_run_save_pipeline(
343
344
  requirements: set[str],
344
345
  save_pipeline: SavePipeline | None,
345
346
  verification: dict[str, Any],
347
+ publication: BatchPublication,
346
348
  ) -> Any:
347
349
  pipeline = save_pipeline or SavePipeline()
348
350
  quality_policy = _quality_policy(normalized["quality"])
@@ -356,11 +358,11 @@ def _apply_commands_run_save_pipeline(
356
358
  )
357
359
  quality_report = pipeline.run(
358
360
  candidate_data,
359
- output_path=None if normalized["dryRun"] else output_path,
361
+ output_path=None,
360
362
  quality=quality_policy,
361
363
  before=input_path,
362
364
  reference_document=document,
363
- publish="never" if normalized["dryRun"] else "on_pass",
365
+ publish="never",
364
366
  source_label="agent.apply_document_commands",
365
367
  )
366
368
  verification["savePipeline"] = quality_report.to_dict()
@@ -380,4 +382,8 @@ def _apply_commands_run_save_pipeline(
380
382
  raise AgentContractError(
381
383
  "verification_failed", "SavePipeline rejected the candidate", target="savePipeline"
382
384
  )
385
+ publication.assert_current_source()
386
+ if not normalized["dryRun"]:
387
+ quality_report.output_path = publication.publish(candidate_data, pipeline, verification)
388
+ verification["savePipeline"] = quality_report.to_dict()
383
389
  return quality_report