docagent-cli 0.0.35__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 (300) hide show
  1. docagent_cli/__init__.py +36 -0
  2. docagent_cli/__main__.py +6 -0
  3. docagent_cli/_ask_user_types.py +90 -0
  4. docagent_cli/_cli_context.py +27 -0
  5. docagent_cli/_debug.py +52 -0
  6. docagent_cli/_env_vars.py +56 -0
  7. docagent_cli/_server_config.py +352 -0
  8. docagent_cli/_session_stats.py +114 -0
  9. docagent_cli/_testing_models.py +144 -0
  10. docagent_cli/_version.py +17 -0
  11. docagent_cli/agent.py +1193 -0
  12. docagent_cli/app.py +4979 -0
  13. docagent_cli/app.tcss +283 -0
  14. docagent_cli/ask_user.py +301 -0
  15. docagent_cli/built_in_skills/__init__.py +5 -0
  16. docagent_cli/built_in_skills/doc-coauthoring/SKILL.md +375 -0
  17. docagent_cli/built_in_skills/docx/LICENSE.txt +30 -0
  18. docagent_cli/built_in_skills/docx/SKILL.md +590 -0
  19. docagent_cli/built_in_skills/docx/scripts/__init__.py +1 -0
  20. docagent_cli/built_in_skills/docx/scripts/accept_changes.py +135 -0
  21. docagent_cli/built_in_skills/docx/scripts/comment.py +318 -0
  22. docagent_cli/built_in_skills/docx/scripts/office/helpers/__init__.py +0 -0
  23. docagent_cli/built_in_skills/docx/scripts/office/helpers/merge_runs.py +199 -0
  24. docagent_cli/built_in_skills/docx/scripts/office/helpers/simplify_redlines.py +197 -0
  25. docagent_cli/built_in_skills/docx/scripts/office/pack.py +159 -0
  26. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  27. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  28. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  29. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  30. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  31. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  32. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  33. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  34. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  35. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  36. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  37. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  38. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  39. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  40. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  41. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  42. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  43. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  44. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  45. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  46. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  47. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  48. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  49. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  50. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  51. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  52. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  53. docagent_cli/built_in_skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  54. docagent_cli/built_in_skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  55. docagent_cli/built_in_skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  56. docagent_cli/built_in_skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  57. docagent_cli/built_in_skills/docx/scripts/office/schemas/mce/mc.xsd +75 -0
  58. docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
  59. docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
  60. docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
  61. docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
  62. docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
  63. docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  64. docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
  65. docagent_cli/built_in_skills/docx/scripts/office/soffice.py +183 -0
  66. docagent_cli/built_in_skills/docx/scripts/office/unpack.py +132 -0
  67. docagent_cli/built_in_skills/docx/scripts/office/validate.py +111 -0
  68. docagent_cli/built_in_skills/docx/scripts/office/validators/__init__.py +15 -0
  69. docagent_cli/built_in_skills/docx/scripts/office/validators/base.py +847 -0
  70. docagent_cli/built_in_skills/docx/scripts/office/validators/docx.py +446 -0
  71. docagent_cli/built_in_skills/docx/scripts/office/validators/pptx.py +275 -0
  72. docagent_cli/built_in_skills/docx/scripts/office/validators/redlining.py +247 -0
  73. docagent_cli/built_in_skills/docx/scripts/templates/comments.xml +3 -0
  74. docagent_cli/built_in_skills/docx/scripts/templates/commentsExtended.xml +3 -0
  75. docagent_cli/built_in_skills/docx/scripts/templates/commentsExtensible.xml +3 -0
  76. docagent_cli/built_in_skills/docx/scripts/templates/commentsIds.xml +3 -0
  77. docagent_cli/built_in_skills/docx/scripts/templates/people.xml +3 -0
  78. docagent_cli/built_in_skills/pdf/LICENSE.txt +30 -0
  79. docagent_cli/built_in_skills/pdf/SKILL.md +314 -0
  80. docagent_cli/built_in_skills/pdf/forms.md +294 -0
  81. docagent_cli/built_in_skills/pdf/reference.md +612 -0
  82. docagent_cli/built_in_skills/pdf/scripts/check_bounding_boxes.py +65 -0
  83. docagent_cli/built_in_skills/pdf/scripts/check_fillable_fields.py +11 -0
  84. docagent_cli/built_in_skills/pdf/scripts/convert_pdf_to_images.py +33 -0
  85. docagent_cli/built_in_skills/pdf/scripts/create_validation_image.py +37 -0
  86. docagent_cli/built_in_skills/pdf/scripts/extract_form_field_info.py +122 -0
  87. docagent_cli/built_in_skills/pdf/scripts/extract_form_structure.py +115 -0
  88. docagent_cli/built_in_skills/pdf/scripts/fill_fillable_fields.py +98 -0
  89. docagent_cli/built_in_skills/pdf/scripts/fill_pdf_form_with_annotations.py +107 -0
  90. docagent_cli/built_in_skills/pptx/LICENSE.txt +30 -0
  91. docagent_cli/built_in_skills/pptx/SKILL.md +232 -0
  92. docagent_cli/built_in_skills/pptx/editing.md +205 -0
  93. docagent_cli/built_in_skills/pptx/pptxgenjs.md +420 -0
  94. docagent_cli/built_in_skills/pptx/scripts/__init__.py +0 -0
  95. docagent_cli/built_in_skills/pptx/scripts/add_slide.py +195 -0
  96. docagent_cli/built_in_skills/pptx/scripts/clean.py +286 -0
  97. docagent_cli/built_in_skills/pptx/scripts/office/helpers/__init__.py +0 -0
  98. docagent_cli/built_in_skills/pptx/scripts/office/helpers/merge_runs.py +199 -0
  99. docagent_cli/built_in_skills/pptx/scripts/office/helpers/simplify_redlines.py +197 -0
  100. docagent_cli/built_in_skills/pptx/scripts/office/pack.py +159 -0
  101. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  102. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  103. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  104. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  105. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  106. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  107. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  108. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  109. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  110. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  111. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  112. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  113. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  114. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  115. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  116. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  117. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  118. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  119. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  120. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  121. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  122. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  123. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  124. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  125. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  126. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  127. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  128. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  129. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  130. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  131. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  132. docagent_cli/built_in_skills/pptx/scripts/office/schemas/mce/mc.xsd +75 -0
  133. docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
  134. docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
  135. docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
  136. docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
  137. docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
  138. docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  139. docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
  140. docagent_cli/built_in_skills/pptx/scripts/office/soffice.py +183 -0
  141. docagent_cli/built_in_skills/pptx/scripts/office/unpack.py +132 -0
  142. docagent_cli/built_in_skills/pptx/scripts/office/validate.py +111 -0
  143. docagent_cli/built_in_skills/pptx/scripts/office/validators/__init__.py +15 -0
  144. docagent_cli/built_in_skills/pptx/scripts/office/validators/base.py +847 -0
  145. docagent_cli/built_in_skills/pptx/scripts/office/validators/docx.py +446 -0
  146. docagent_cli/built_in_skills/pptx/scripts/office/validators/pptx.py +275 -0
  147. docagent_cli/built_in_skills/pptx/scripts/office/validators/redlining.py +247 -0
  148. docagent_cli/built_in_skills/pptx/scripts/thumbnail.py +289 -0
  149. docagent_cli/built_in_skills/remember/SKILL.md +118 -0
  150. docagent_cli/built_in_skills/skill-creator/LICENSE.txt +202 -0
  151. docagent_cli/built_in_skills/skill-creator/SKILL.md +485 -0
  152. docagent_cli/built_in_skills/skill-creator/agents/analyzer.md +274 -0
  153. docagent_cli/built_in_skills/skill-creator/agents/comparator.md +202 -0
  154. docagent_cli/built_in_skills/skill-creator/agents/grader.md +223 -0
  155. docagent_cli/built_in_skills/skill-creator/assets/eval_review.html +146 -0
  156. docagent_cli/built_in_skills/skill-creator/eval-viewer/generate_review.py +471 -0
  157. docagent_cli/built_in_skills/skill-creator/eval-viewer/viewer.html +1325 -0
  158. docagent_cli/built_in_skills/skill-creator/references/schemas.md +430 -0
  159. docagent_cli/built_in_skills/skill-creator/scripts/__init__.py +0 -0
  160. docagent_cli/built_in_skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  161. docagent_cli/built_in_skills/skill-creator/scripts/generate_report.py +326 -0
  162. docagent_cli/built_in_skills/skill-creator/scripts/improve_description.py +247 -0
  163. docagent_cli/built_in_skills/skill-creator/scripts/package_skill.py +136 -0
  164. docagent_cli/built_in_skills/skill-creator/scripts/quick_validate.py +103 -0
  165. docagent_cli/built_in_skills/skill-creator/scripts/run_eval.py +310 -0
  166. docagent_cli/built_in_skills/skill-creator/scripts/run_loop.py +328 -0
  167. docagent_cli/built_in_skills/skill-creator/scripts/utils.py +47 -0
  168. docagent_cli/built_in_skills/theme-factory/LICENSE.txt +202 -0
  169. docagent_cli/built_in_skills/theme-factory/SKILL.md +59 -0
  170. docagent_cli/built_in_skills/theme-factory/theme-showcase.pdf +0 -0
  171. docagent_cli/built_in_skills/theme-factory/themes/arctic-frost.md +19 -0
  172. docagent_cli/built_in_skills/theme-factory/themes/botanical-garden.md +19 -0
  173. docagent_cli/built_in_skills/theme-factory/themes/desert-rose.md +19 -0
  174. docagent_cli/built_in_skills/theme-factory/themes/forest-canopy.md +19 -0
  175. docagent_cli/built_in_skills/theme-factory/themes/golden-hour.md +19 -0
  176. docagent_cli/built_in_skills/theme-factory/themes/midnight-galaxy.md +19 -0
  177. docagent_cli/built_in_skills/theme-factory/themes/modern-minimalist.md +19 -0
  178. docagent_cli/built_in_skills/theme-factory/themes/ocean-depths.md +19 -0
  179. docagent_cli/built_in_skills/theme-factory/themes/sunset-boulevard.md +19 -0
  180. docagent_cli/built_in_skills/theme-factory/themes/tech-innovation.md +19 -0
  181. docagent_cli/built_in_skills/xlsx/LICENSE.txt +30 -0
  182. docagent_cli/built_in_skills/xlsx/SKILL.md +292 -0
  183. docagent_cli/built_in_skills/xlsx/scripts/office/helpers/__init__.py +0 -0
  184. docagent_cli/built_in_skills/xlsx/scripts/office/helpers/merge_runs.py +199 -0
  185. docagent_cli/built_in_skills/xlsx/scripts/office/helpers/simplify_redlines.py +197 -0
  186. docagent_cli/built_in_skills/xlsx/scripts/office/pack.py +159 -0
  187. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  188. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  189. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  190. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  191. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  192. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  193. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  194. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  195. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  196. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  197. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  198. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  199. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  200. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  201. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  202. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  203. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  204. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  205. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  206. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  207. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  208. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  209. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  210. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  211. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  212. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  213. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  214. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  215. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  216. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  217. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  218. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/mce/mc.xsd +75 -0
  219. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
  220. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
  221. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
  222. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
  223. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
  224. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  225. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
  226. docagent_cli/built_in_skills/xlsx/scripts/office/soffice.py +183 -0
  227. docagent_cli/built_in_skills/xlsx/scripts/office/unpack.py +132 -0
  228. docagent_cli/built_in_skills/xlsx/scripts/office/validate.py +111 -0
  229. docagent_cli/built_in_skills/xlsx/scripts/office/validators/__init__.py +15 -0
  230. docagent_cli/built_in_skills/xlsx/scripts/office/validators/base.py +847 -0
  231. docagent_cli/built_in_skills/xlsx/scripts/office/validators/docx.py +446 -0
  232. docagent_cli/built_in_skills/xlsx/scripts/office/validators/pptx.py +275 -0
  233. docagent_cli/built_in_skills/xlsx/scripts/office/validators/redlining.py +247 -0
  234. docagent_cli/built_in_skills/xlsx/scripts/recalc.py +184 -0
  235. docagent_cli/clipboard.py +128 -0
  236. docagent_cli/command_registry.py +284 -0
  237. docagent_cli/config.py +2418 -0
  238. docagent_cli/configurable_model.py +162 -0
  239. docagent_cli/default_agent_prompt.md +12 -0
  240. docagent_cli/editor.py +142 -0
  241. docagent_cli/file_ops.py +473 -0
  242. docagent_cli/formatting.py +28 -0
  243. docagent_cli/hooks.py +206 -0
  244. docagent_cli/input.py +787 -0
  245. docagent_cli/integrations/__init__.py +1 -0
  246. docagent_cli/integrations/sandbox_factory.py +873 -0
  247. docagent_cli/integrations/sandbox_provider.py +71 -0
  248. docagent_cli/local_context.py +718 -0
  249. docagent_cli/main.py +1641 -0
  250. docagent_cli/mcp_tools.py +707 -0
  251. docagent_cli/mcp_trust.py +168 -0
  252. docagent_cli/media_utils.py +478 -0
  253. docagent_cli/model_config.py +1620 -0
  254. docagent_cli/non_interactive.py +948 -0
  255. docagent_cli/offload.py +371 -0
  256. docagent_cli/output.py +69 -0
  257. docagent_cli/project_utils.py +188 -0
  258. docagent_cli/py.typed +0 -0
  259. docagent_cli/remote_client.py +515 -0
  260. docagent_cli/server.py +520 -0
  261. docagent_cli/server_graph.py +196 -0
  262. docagent_cli/server_manager.py +365 -0
  263. docagent_cli/sessions.py +1262 -0
  264. docagent_cli/skills/__init__.py +18 -0
  265. docagent_cli/skills/commands.py +1090 -0
  266. docagent_cli/skills/load.py +192 -0
  267. docagent_cli/subagents.py +173 -0
  268. docagent_cli/system_prompt.md +247 -0
  269. docagent_cli/textual_adapter.py +1352 -0
  270. docagent_cli/theme.py +842 -0
  271. docagent_cli/token_state.py +31 -0
  272. docagent_cli/tool_display.py +298 -0
  273. docagent_cli/tools.py +236 -0
  274. docagent_cli/ui.py +420 -0
  275. docagent_cli/unicode_security.py +516 -0
  276. docagent_cli/update_check.py +454 -0
  277. docagent_cli/widgets/__init__.py +9 -0
  278. docagent_cli/widgets/_links.py +63 -0
  279. docagent_cli/widgets/approval.py +442 -0
  280. docagent_cli/widgets/ask_user.py +398 -0
  281. docagent_cli/widgets/autocomplete.py +691 -0
  282. docagent_cli/widgets/chat_input.py +1827 -0
  283. docagent_cli/widgets/diff.py +248 -0
  284. docagent_cli/widgets/history.py +188 -0
  285. docagent_cli/widgets/loading.py +177 -0
  286. docagent_cli/widgets/mcp_viewer.py +362 -0
  287. docagent_cli/widgets/message_store.py +675 -0
  288. docagent_cli/widgets/messages.py +1751 -0
  289. docagent_cli/widgets/model_selector.py +964 -0
  290. docagent_cli/widgets/status.py +372 -0
  291. docagent_cli/widgets/theme_selector.py +164 -0
  292. docagent_cli/widgets/thread_selector.py +1905 -0
  293. docagent_cli/widgets/tool_renderers.py +148 -0
  294. docagent_cli/widgets/tool_widgets.py +274 -0
  295. docagent_cli/widgets/welcome.py +339 -0
  296. docagent_cli-0.0.35.data/data/docagent_cli/default_agent_prompt.md +12 -0
  297. docagent_cli-0.0.35.dist-info/METADATA +200 -0
  298. docagent_cli-0.0.35.dist-info/RECORD +300 -0
  299. docagent_cli-0.0.35.dist-info/WHEEL +4 -0
  300. docagent_cli-0.0.35.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,948 @@
1
+ """Non-interactive execution mode for docagent CLI.
2
+
3
+ Provides `run_non_interactive` which runs a single user task against the
4
+ agent graph, streams results to stdout, and exits with an appropriate code.
5
+
6
+ The agent runs inside a `langgraph dev` server subprocess, connected via
7
+ the `RemoteAgent` client (see `server_manager.server_session`).
8
+
9
+ Shell commands are gated by an optional allow-list (`--shell-allow-list`):
10
+
11
+ - Not set → shell disabled, all other tool calls auto-approved.
12
+ - `recommended` or explicit list → shell enabled, commands validated
13
+ against the list; non-shell tools approved unconditionally.
14
+ - `all` → shell enabled, any command allowed, all tools auto-approved.
15
+
16
+ An optional quiet mode (`--quiet` / `-q`) redirects all console output to
17
+ stderr, leaving stdout exclusively for the agent's response text.
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import logging
23
+ import sys
24
+ import threading
25
+ import time
26
+ from dataclasses import dataclass, field
27
+ from typing import TYPE_CHECKING, Any, cast
28
+
29
+ from langchain.agents.middleware.human_in_the_loop import ActionRequest, HITLRequest
30
+ from langchain_core.messages import AIMessage, ToolMessage
31
+ from langgraph.types import Command, Interrupt
32
+ from pydantic import TypeAdapter, ValidationError
33
+ from rich.console import Console
34
+ from rich.live import Live
35
+ from rich.markup import escape as escape_markup
36
+ from rich.spinner import Spinner as RichSpinner
37
+ from rich.style import Style
38
+ from rich.text import Text
39
+
40
+ from docagent_cli._version import __version__
41
+ from docagent_cli.agent import DEFAULT_AGENT_NAME
42
+ from docagent_cli.config import (
43
+ SHELL_ALLOW_ALL,
44
+ SHELL_TOOL_NAMES,
45
+ build_langsmith_thread_url,
46
+ create_model,
47
+ is_shell_command_allowed,
48
+ settings,
49
+ )
50
+ from docagent_cli.file_ops import FileOpTracker
51
+ from docagent_cli.hooks import dispatch_hook, dispatch_hook_fire_and_forget
52
+ from docagent_cli.model_config import ModelConfigError
53
+ from docagent_cli.sessions import generate_thread_id
54
+ from docagent_cli.textual_adapter import SessionStats, print_usage_table
55
+ from docagent_cli.unicode_security import (
56
+ check_url_safety,
57
+ detect_dangerous_unicode,
58
+ format_warning_detail,
59
+ iter_string_values,
60
+ looks_like_url_key,
61
+ summarize_issues,
62
+ )
63
+
64
+ if TYPE_CHECKING:
65
+ from langchain_core.runnables import RunnableConfig
66
+
67
+ logger = logging.getLogger(__name__)
68
+
69
+
70
+ class HITLIterationLimitError(RuntimeError):
71
+ """Raised when the HITL interrupt loop exceeds `_MAX_HITL_ITERATIONS` rounds."""
72
+
73
+
74
+ _HITL_REQUEST_ADAPTER = TypeAdapter(HITLRequest)
75
+
76
+ _STREAM_CHUNK_LENGTH = 3
77
+ """Expected element counts for the tuples emitted by agent.astream.
78
+
79
+ Stream chunks are 3-tuples: (namespace, stream_mode, data).
80
+ """
81
+
82
+ _MESSAGE_DATA_LENGTH = 2
83
+ """Message-mode data is a 2-tuple: (message_obj, metadata)."""
84
+
85
+ _MAX_HITL_ITERATIONS = 50
86
+ """Safety cap on the number of HITL interrupt round-trips to prevent infinite
87
+ loops (e.g. when the agent keeps retrying rejected commands)."""
88
+
89
+
90
+ def _write_text(text: str) -> None:
91
+ """Write agent response text to stdout (without a trailing newline).
92
+
93
+ Uses `sys.stdout` directly (rather than the Rich Console) so that agent
94
+ response text always appears on stdout, even in quiet mode where the
95
+ Console is redirected to stderr.
96
+
97
+ Args:
98
+ text: The text string to write.
99
+ """
100
+ sys.stdout.write(text)
101
+ sys.stdout.flush()
102
+
103
+
104
+ def _write_newline() -> None:
105
+ """Write a newline to stdout (and flush)."""
106
+ sys.stdout.write("\n")
107
+ sys.stdout.flush()
108
+
109
+
110
+ class _ConsoleSpinner:
111
+ """Animated spinner for non-interactive verbose output.
112
+
113
+ Uses Rich's `Live` display with a transient braille-dot spinner that
114
+ disappears when stopped, keeping terminal output clean.
115
+ """
116
+
117
+ def __init__(self, console: Console) -> None:
118
+ self._console = console
119
+ self._live: Live | None = None
120
+
121
+ def start(self, message: str = "Working...") -> None:
122
+ """Start the spinner with the given message.
123
+
124
+ No-op if the spinner is already running. Fails silently if the console
125
+ cannot support live display.
126
+
127
+ Args:
128
+ message: Status text to display next to the spinner.
129
+ """
130
+ if self._live is not None:
131
+ return
132
+ renderable = RichSpinner(
133
+ "dots",
134
+ text=Text(f" {message}", style="dim"),
135
+ style="dim",
136
+ )
137
+ try:
138
+ self._live = Live(renderable, console=self._console, transient=True)
139
+ self._live.start()
140
+ except (AttributeError, TypeError, OSError) as exc:
141
+ logger.warning("Spinner start failed: %s", exc)
142
+ self._live = None
143
+
144
+ def stop(self) -> None:
145
+ """Stop the spinner if running. Can be restarted with `start`."""
146
+ if self._live is not None:
147
+ try:
148
+ self._live.stop()
149
+ except (AttributeError, TypeError, OSError) as exc:
150
+ logger.warning("Spinner stop failed: %s", exc)
151
+ finally:
152
+ self._live = None
153
+
154
+
155
+ @dataclass
156
+ class StreamState:
157
+ """Mutable state accumulated while iterating over the agent stream."""
158
+
159
+ quiet: bool = False
160
+ """When `True`, diagnostic formatting that would otherwise go to stdout
161
+ (e.g. separator newlines before tool notifications) is suppressed so that
162
+ stdout contains only agent response text."""
163
+
164
+ stream: bool = True
165
+ """When `True` (default), text chunks are written to stdout as they arrive.
166
+
167
+ When `False`, text is buffered in `full_response` and flushed after the
168
+ agent finishes.
169
+ """
170
+
171
+ full_response: list[str] = field(default_factory=list)
172
+ """Accumulated text fragments from the AI message stream."""
173
+
174
+ tool_call_buffers: dict[int | str, dict[str, str | None]] = field(
175
+ default_factory=dict
176
+ )
177
+ """Maps a tool-call index or ID to its name/ID metadata for in-progress
178
+ tool calls."""
179
+
180
+ pending_interrupts: dict[str, HITLRequest] = field(default_factory=dict)
181
+ """Maps interrupt IDs to their validated HITL requests that are awaiting
182
+ decisions."""
183
+
184
+ hitl_response: dict[str, dict[str, list[dict[str, str]]]] = field(
185
+ default_factory=dict
186
+ )
187
+ """Maps interrupt IDs to dicts containing a `'decisions'` key with a list of
188
+ decision dicts (each having a `'type'` key of `'approve'` or `'reject'`).
189
+
190
+ Used to resume the agent after HITL processing.
191
+ """
192
+
193
+ interrupt_occurred: bool = False
194
+ """Flag indicating whether any HITL interrupt was received during the
195
+ current stream pass."""
196
+
197
+ stats: SessionStats = field(default_factory=SessionStats)
198
+ """Accumulated model usage stats for this stream."""
199
+
200
+ spinner: _ConsoleSpinner | None = None
201
+ """Optional animated spinner shown during agent work in verbose mode."""
202
+
203
+
204
+ @dataclass
205
+ class ThreadUrlLookupState:
206
+ """Best-effort background LangSmith thread URL lookup state.
207
+
208
+ Thread safety: the background thread sets `url` then calls `done.set()`.
209
+ Consumers must check `done.is_set()` before reading `url`.
210
+ """
211
+
212
+ done: threading.Event = field(default_factory=threading.Event)
213
+ url: str | None = None
214
+
215
+
216
+ def _start_langsmith_thread_url_lookup(thread_id: str) -> ThreadUrlLookupState:
217
+ """Start background LangSmith URL resolution without blocking.
218
+
219
+ Args:
220
+ thread_id: Thread identifier to resolve.
221
+
222
+ Returns:
223
+ Mutable lookup state whose completion can be checked later.
224
+ """
225
+ state = ThreadUrlLookupState()
226
+
227
+ def _resolve() -> None:
228
+ try:
229
+ state.url = build_langsmith_thread_url(thread_id)
230
+ except Exception: # build_langsmith_thread_url already handles known errors
231
+ logger.debug(
232
+ "Could not resolve LangSmith thread URL for '%s'",
233
+ thread_id,
234
+ exc_info=True,
235
+ )
236
+ finally:
237
+ state.done.set()
238
+
239
+ threading.Thread(target=_resolve, daemon=True).start()
240
+ return state
241
+
242
+
243
+ def _process_interrupts(
244
+ data: dict[str, list[Interrupt]],
245
+ state: StreamState,
246
+ console: Console,
247
+ ) -> None:
248
+ """Extract HITL interrupts from an `updates` chunk and record them.
249
+
250
+ Args:
251
+ data: The `updates` dict that contains an `__interrupt__` key.
252
+ state: Stream state to update with new pending interrupts.
253
+ console: Rich console for user-visible warnings.
254
+ """
255
+ interrupts = data["__interrupt__"]
256
+ if interrupts:
257
+ for interrupt_obj in interrupts:
258
+ try:
259
+ validated_request = _HITL_REQUEST_ADAPTER.validate_python(
260
+ interrupt_obj.value
261
+ )
262
+ except ValidationError:
263
+ logger.warning(
264
+ "Rejecting malformed HITL interrupt %s (raw value: %r)",
265
+ interrupt_obj.id,
266
+ interrupt_obj.value,
267
+ )
268
+ console.print(
269
+ f"[yellow]Warning: Received malformed tool approval "
270
+ f"request (interrupt {interrupt_obj.id}). Rejecting.[/yellow]"
271
+ )
272
+ # Fail-closed: record a reject decision for malformed interrupts
273
+
274
+ state.hitl_response[interrupt_obj.id] = {
275
+ "decisions": [{"type": "reject", "message": "Malformed interrupt"}]
276
+ }
277
+ continue
278
+ state.pending_interrupts[interrupt_obj.id] = validated_request
279
+ state.interrupt_occurred = True
280
+ dispatch_hook_fire_and_forget("input.required", {})
281
+
282
+
283
+ def _process_ai_message(
284
+ message_obj: AIMessage,
285
+ state: StreamState,
286
+ console: Console,
287
+ ) -> None:
288
+ """Extract text and tool-call blocks from an AI message and render them.
289
+
290
+ When streaming is enabled, text blocks are written to stdout immediately;
291
+ otherwise they are accumulated in `state.full_response` for deferred
292
+ output. Tool-call blocks are buffered and their names are printed to the
293
+ console.
294
+
295
+ Args:
296
+ message_obj: The `AIMessage` received from the stream.
297
+ state: Stream state for accumulating response text and tool-call buffers.
298
+ console: Rich console for formatted output.
299
+ """
300
+ # Extract token usage for stats accumulation
301
+ usage = getattr(message_obj, "usage_metadata", None)
302
+ if usage:
303
+ input_toks = usage.get("input_tokens", 0)
304
+ output_toks = usage.get("output_tokens", 0)
305
+ total_toks = usage.get("total_tokens", 0)
306
+ active_model = settings.model_name or ""
307
+ if input_toks or output_toks:
308
+ state.stats.record_request(active_model, input_toks, output_toks)
309
+ elif total_toks:
310
+ state.stats.record_request(active_model, total_toks, 0)
311
+
312
+ if not hasattr(message_obj, "content_blocks"):
313
+ logger.debug("AIMessage missing content_blocks attribute, skipping")
314
+ return
315
+ for block in message_obj.content_blocks:
316
+ if not isinstance(block, dict):
317
+ continue
318
+ block_type = block.get("type")
319
+ if block_type == "text":
320
+ text = block.get("text", "")
321
+ if text:
322
+ if state.stream:
323
+ if state.spinner:
324
+ state.spinner.stop()
325
+ _write_text(text)
326
+ state.full_response.append(text)
327
+ elif block_type in {"tool_call_chunk", "tool_call"}:
328
+ chunk_name = block.get("name")
329
+ chunk_id = block.get("id")
330
+ chunk_index = block.get("index")
331
+
332
+ if chunk_index is not None:
333
+ buffer_key: int | str = chunk_index
334
+ elif chunk_id is not None:
335
+ buffer_key = chunk_id
336
+ else:
337
+ buffer_key = f"unknown-{len(state.tool_call_buffers)}"
338
+
339
+ if buffer_key not in state.tool_call_buffers:
340
+ state.tool_call_buffers[buffer_key] = {"name": None, "id": None}
341
+ if chunk_name:
342
+ state.tool_call_buffers[buffer_key]["name"] = chunk_name
343
+ if state.spinner:
344
+ state.spinner.stop()
345
+ if state.full_response and not state.quiet:
346
+ _write_newline()
347
+ console.print(
348
+ f"[dim]🔧 Calling tool: {escape_markup(chunk_name)}[/dim]",
349
+ highlight=False,
350
+ )
351
+
352
+
353
+ def _process_message_chunk(
354
+ data: tuple[AIMessage | ToolMessage, dict[str, str]],
355
+ state: StreamState,
356
+ console: Console,
357
+ file_op_tracker: FileOpTracker,
358
+ ) -> None:
359
+ """Handle a `messages`-mode chunk from the stream.
360
+
361
+ Dispatches to AI-message or tool-message processing depending on the
362
+ message type.
363
+
364
+ Args:
365
+ data: A 2-tuple of `(message_obj, metadata)` from the messages
366
+ stream mode.
367
+ state: Shared stream state.
368
+ console: Rich console for formatted output.
369
+ file_op_tracker: Tracker for file-operation diffs.
370
+ """
371
+ if not isinstance(data, tuple) or len(data) != _MESSAGE_DATA_LENGTH:
372
+ logger.debug(
373
+ "Unexpected message-mode data (type=%s), skipping", type(data).__name__
374
+ )
375
+ return
376
+
377
+ message_obj, metadata = data
378
+
379
+ # The summarization middleware injects synthetic messages to compress
380
+ # conversation history for the LLM. These are internal bookkeeping and
381
+ # should not be rendered to the user.
382
+ if metadata and metadata.get("lc_source") == "summarization":
383
+ return
384
+
385
+ if isinstance(message_obj, AIMessage):
386
+ _process_ai_message(message_obj, state, console)
387
+ elif isinstance(message_obj, ToolMessage):
388
+ record = file_op_tracker.complete_with_message(message_obj)
389
+ if record and record.diff:
390
+ if state.spinner:
391
+ state.spinner.stop()
392
+ console.print(
393
+ f"[dim]📝 {escape_markup(record.display_path)}[/dim]",
394
+ highlight=False,
395
+ )
396
+ if state.spinner:
397
+ state.spinner.start()
398
+
399
+
400
+ def _process_stream_chunk(
401
+ chunk: object,
402
+ state: StreamState,
403
+ console: Console,
404
+ file_op_tracker: FileOpTracker,
405
+ ) -> None:
406
+ """Route a single raw stream chunk to the appropriate handler.
407
+
408
+ Only main-agent chunks are processed; sub-agent output is ignored so
409
+ that only top-level content is rendered.
410
+
411
+ Args:
412
+ chunk: A raw element yielded by `agent.astream`.
413
+
414
+ Expected to be a 3-tuple `(namespace, stream_mode, data)` for
415
+ main-agent output.
416
+ state: Shared stream state.
417
+ console: Rich console for formatted output.
418
+ file_op_tracker: Tracker for file-operation diffs.
419
+ """
420
+ if not isinstance(chunk, tuple) or len(chunk) != _STREAM_CHUNK_LENGTH:
421
+ logger.debug(
422
+ "Unexpected stream chunk (type=%s), skipping", type(chunk).__name__
423
+ )
424
+ return
425
+
426
+ namespace, stream_mode, data = chunk
427
+ is_main_agent = not namespace
428
+
429
+ if not is_main_agent:
430
+ return
431
+
432
+ if stream_mode == "updates" and isinstance(data, dict) and "__interrupt__" in data:
433
+ _process_interrupts(cast("dict[str, list[Interrupt]]", data), state, console)
434
+ elif stream_mode == "messages":
435
+ _process_message_chunk(
436
+ cast("tuple[AIMessage | ToolMessage, dict[str, str]]", data),
437
+ state,
438
+ console,
439
+ file_op_tracker,
440
+ )
441
+
442
+
443
+ def _make_hitl_decision(
444
+ action_request: ActionRequest, console: Console
445
+ ) -> dict[str, str]:
446
+ """Decide whether to approve or reject a single action request.
447
+
448
+ This function is only invoked when a restrictive shell allow-list is
449
+ configured (not `all`). When shell is disabled or unrestricted,
450
+ `interrupt_on` is empty and this function is bypassed entirely.
451
+
452
+ Shell tools are always gated: if an allow-list is configured, the command
453
+ is validated against it; if no allow-list is configured, shell commands
454
+ are rejected outright (defense-in-depth — the caller should disable
455
+ shell tools when no allow-list is present, but this function fails
456
+ closed regardless). Non-shell tools are approved unconditionally.
457
+
458
+ Args:
459
+ action_request: The action-request dict emitted by the HITL middleware.
460
+
461
+ Must contain at least a `name` key.
462
+ console: Rich console for status output.
463
+
464
+ Returns:
465
+ Decision dict with a `type` key (`"approve"` or `"reject"`)
466
+ and an optional `message` key with a human-readable explanation.
467
+ """
468
+ for warning in _collect_action_request_warnings(action_request):
469
+ console.print(f"[yellow]Warning:[/yellow] {warning}")
470
+
471
+ action_name = action_request.get("name", "")
472
+
473
+ if action_name in SHELL_TOOL_NAMES:
474
+ if not settings.shell_allow_list:
475
+ command = action_request.get("args", {}).get("command", "")
476
+ console.print(
477
+ f"\n[red]Shell command rejected (no allow-list configured): "
478
+ f"{command}[/red]"
479
+ )
480
+ return {
481
+ "type": "reject",
482
+ "message": (
483
+ "Shell commands are not permitted in non-interactive mode "
484
+ "without a --shell-allow-list. Use --shell-allow-list to "
485
+ "specify allowed commands."
486
+ ),
487
+ }
488
+
489
+ command = action_request.get("args", {}).get("command", "")
490
+
491
+ if is_shell_command_allowed(command, settings.shell_allow_list):
492
+ console.print(f"[dim]✓ Auto-approved: {escape_markup(command)}[/dim]")
493
+ return {"type": "approve"}
494
+
495
+ allowed_list_str = ", ".join(settings.shell_allow_list)
496
+ console.print(f"\n[red]Shell command rejected:[/red] {escape_markup(command)}")
497
+ console.print(
498
+ f"[yellow]Allowed commands:[/yellow] {escape_markup(allowed_list_str)}"
499
+ )
500
+ return {
501
+ "type": "reject",
502
+ "message": (
503
+ f"Command '{command}' is not in the allow-list. "
504
+ f"Allowed commands: {allowed_list_str}. "
505
+ f"Please use allowed commands or try another approach."
506
+ ),
507
+ }
508
+
509
+ console.print(f"[dim]✓ Auto-approved action: {escape_markup(action_name)}[/dim]")
510
+ return {"type": "approve"}
511
+
512
+
513
+ def _collect_action_request_warnings(action_request: ActionRequest) -> list[str]:
514
+ """Collect Unicode/URL safety warnings for one action request.
515
+
516
+ Recursively inspects all nested string values in action arguments.
517
+
518
+ Returns:
519
+ Warning messages for suspicious values in action arguments.
520
+ """
521
+ warnings: list[str] = []
522
+ args = action_request.get("args", {})
523
+ if not isinstance(args, dict):
524
+ return warnings
525
+
526
+ tool_name = str(action_request.get("name", "unknown"))
527
+
528
+ for arg_path, text in iter_string_values(args):
529
+ issues = detect_dangerous_unicode(text)
530
+ if issues:
531
+ warnings.append(
532
+ f"{tool_name}.{arg_path} contains hidden Unicode "
533
+ f"({summarize_issues(issues)})"
534
+ )
535
+
536
+ if looks_like_url_key(arg_path):
537
+ safety = check_url_safety(text)
538
+ if safety.safe:
539
+ continue
540
+ detail = format_warning_detail(safety.warnings)
541
+ if safety.decoded_domain:
542
+ detail = f"{detail}; decoded host: {safety.decoded_domain}"
543
+ warnings.append(f"{tool_name}.{arg_path} URL warning: {detail}")
544
+
545
+ return warnings
546
+
547
+
548
+ def _process_hitl_interrupts(state: StreamState, console: Console) -> None:
549
+ """Iterate over pending HITL interrupts and build approval/rejection responses.
550
+
551
+ After processing, `state.pending_interrupts` is cleared and decisions
552
+ are written into `state.hitl_response` so the agent can be resumed.
553
+
554
+ Args:
555
+ state: Stream state containing the pending interrupts to process.
556
+ console: Rich console for status output.
557
+ """
558
+ current_interrupts = dict(state.pending_interrupts)
559
+ state.pending_interrupts.clear()
560
+
561
+ for interrupt_id, hitl_request in current_interrupts.items():
562
+ decisions = [
563
+ _make_hitl_decision(action_request, console)
564
+ for action_request in hitl_request["action_requests"]
565
+ ]
566
+ state.hitl_response[interrupt_id] = {"decisions": decisions}
567
+
568
+
569
+ async def _stream_agent(
570
+ agent: Any, # noqa: ANN401
571
+ stream_input: dict[str, Any] | Command,
572
+ config: RunnableConfig,
573
+ state: StreamState,
574
+ console: Console,
575
+ file_op_tracker: FileOpTracker,
576
+ ) -> None:
577
+ """Consume the full agent stream and update *state* with results.
578
+
579
+ Args:
580
+ agent: The agent (Pregel or RemoteAgent).
581
+ stream_input: Either the initial user message dict or a
582
+ `Command(resume=...)` for HITL continuation.
583
+ config: LangGraph runnable config (thread ID, metadata, etc.).
584
+ state: Shared stream state.
585
+ console: Rich console for formatted output.
586
+ file_op_tracker: Tracker for file-operation diffs.
587
+ """
588
+ if state.spinner:
589
+ state.spinner.start()
590
+ try:
591
+ async for chunk in agent.astream(
592
+ stream_input,
593
+ stream_mode=["messages", "updates"],
594
+ subgraphs=True,
595
+ config=config,
596
+ durability="exit",
597
+ ):
598
+ _process_stream_chunk(chunk, state, console, file_op_tracker)
599
+ finally:
600
+ if state.spinner:
601
+ state.spinner.stop()
602
+
603
+
604
+ async def _run_agent_loop(
605
+ agent: Any, # noqa: ANN401
606
+ message: str,
607
+ config: RunnableConfig,
608
+ console: Console,
609
+ file_op_tracker: FileOpTracker,
610
+ *,
611
+ quiet: bool = False,
612
+ stream: bool = True,
613
+ thread_url_lookup: ThreadUrlLookupState | None = None,
614
+ ) -> None:
615
+ """Run the agent and handle HITL interrupts until the task completes.
616
+
617
+ The loop processes at most `_MAX_HITL_ITERATIONS` rounds to prevent
618
+ runaway retries (e.g. the agent repeatedly attempting rejected commands).
619
+
620
+ Args:
621
+ agent: The agent (Pregel or RemoteAgent).
622
+ message: The user's task message.
623
+ config: LangGraph runnable config.
624
+ console: Rich console for formatted output.
625
+ file_op_tracker: Tracker for file-operation diffs.
626
+ quiet: Suppress diagnostic formatting on stdout.
627
+ stream: When `True`, text is written to stdout as it arrives.
628
+
629
+ When `False`, the full response is buffered and flushed at
630
+ the end.
631
+ thread_url_lookup: Optional non-blocking lookup state for rendering
632
+ a fast-follow LangSmith thread link.
633
+
634
+ Raises:
635
+ HITLIterationLimitError: If the HITL iteration limit is exceeded.
636
+ """
637
+ spinner = None if quiet else _ConsoleSpinner(console)
638
+ state = StreamState(quiet=quiet, stream=stream, spinner=spinner)
639
+ stream_input: dict[str, Any] | Command = {
640
+ "messages": [{"role": "user", "content": message}]
641
+ }
642
+
643
+ thread_id = config.get("configurable", {}).get("thread_id", "")
644
+ await dispatch_hook("session.start", {"thread_id": thread_id})
645
+
646
+ start_time = time.monotonic()
647
+
648
+ # Initial stream
649
+ await _stream_agent(agent, stream_input, config, state, console, file_op_tracker)
650
+
651
+ # Handle HITL interrupts
652
+ iterations = 0
653
+ while state.interrupt_occurred:
654
+ iterations += 1
655
+ if iterations > _MAX_HITL_ITERATIONS:
656
+ msg = (
657
+ f"Exceeded {_MAX_HITL_ITERATIONS} HITL interrupt rounds. "
658
+ "The agent may be stuck retrying rejected commands."
659
+ )
660
+ raise HITLIterationLimitError(msg)
661
+ state.interrupt_occurred = False
662
+ state.hitl_response.clear()
663
+ _process_hitl_interrupts(state, console)
664
+ stream_input = Command(resume=state.hitl_response)
665
+ await _stream_agent(
666
+ agent, stream_input, config, state, console, file_op_tracker
667
+ )
668
+
669
+ wall_time = time.monotonic() - start_time
670
+
671
+ if state.full_response:
672
+ if not state.stream:
673
+ _write_text("".join(state.full_response))
674
+ _write_newline()
675
+
676
+ if not quiet:
677
+ console.print()
678
+ if (
679
+ thread_url_lookup is not None
680
+ and thread_url_lookup.done.is_set()
681
+ and thread_url_lookup.url
682
+ ):
683
+ link_text = Text("View in LangSmith: ", style="dim")
684
+ link_text.append(
685
+ thread_url_lookup.url,
686
+ style=Style(dim=True, link=thread_url_lookup.url),
687
+ )
688
+ console.print(link_text)
689
+ console.print("[green]✓ Task completed[/green]")
690
+ print_usage_table(state.stats, wall_time, console)
691
+
692
+ await dispatch_hook("task.complete", {"thread_id": thread_id})
693
+ await dispatch_hook("session.end", {"thread_id": thread_id})
694
+
695
+
696
+ def _build_non_interactive_header(
697
+ assistant_id: str,
698
+ thread_id: str,
699
+ *,
700
+ include_thread_link: bool = False,
701
+ ) -> Text:
702
+ """Build the non-interactive mode header with model, agent, and thread info.
703
+
704
+ By default, this function avoids LangSmith network lookups and renders the
705
+ thread ID as plain text. Callers can opt in to hyperlink resolution.
706
+
707
+ Args:
708
+ assistant_id: Agent identifier.
709
+ thread_id: Thread identifier.
710
+ include_thread_link: Whether to resolve and render a LangSmith link for
711
+ the thread ID.
712
+
713
+ Returns:
714
+ Rich Text object with the formatted header line.
715
+ """
716
+ default_label = " (default)" if assistant_id == DEFAULT_AGENT_NAME else ""
717
+ parts: list[tuple[str, str | Style]] = [
718
+ (f"CLI: v{__version__}", "dim"),
719
+ (" | ", "dim"),
720
+ (f"Agent: {assistant_id}{default_label}", "dim"),
721
+ ]
722
+
723
+ if settings.model_name:
724
+ parts.extend([(" | ", "dim"), (f"Model: {settings.model_name}", "dim")])
725
+
726
+ parts.append((" | ", "dim"))
727
+
728
+ thread_url = build_langsmith_thread_url(thread_id) if include_thread_link else None
729
+ if thread_url:
730
+ parts.extend(
731
+ [
732
+ ("Thread: ", "dim"),
733
+ (thread_id, Style(dim=True, link=thread_url)),
734
+ ]
735
+ )
736
+ else:
737
+ parts.append((f"Thread: {thread_id}", "dim"))
738
+
739
+ return Text.assemble(*parts)
740
+
741
+
742
+ async def run_non_interactive(
743
+ message: str,
744
+ assistant_id: str = "agent",
745
+ model_name: str | None = None,
746
+ model_params: dict[str, Any] | None = None,
747
+ sandbox_type: str = "none", # str (not None) to match argparse choices
748
+ sandbox_id: str | None = None,
749
+ sandbox_setup: str | None = None,
750
+ *,
751
+ profile_override: dict[str, Any] | None = None,
752
+ quiet: bool = False,
753
+ stream: bool = True,
754
+ mcp_config_path: str | None = None,
755
+ no_mcp: bool = False,
756
+ trust_project_mcp: bool = False,
757
+ ) -> int:
758
+ """Run a single task non-interactively and exit.
759
+
760
+ The agent is created with `interactive=False`, which tailors the system
761
+ prompt for autonomous headless execution (no clarification questions,
762
+ reasonable assumptions).
763
+
764
+ Shell access and auto-approval are controlled by `--shell-allow-list`:
765
+
766
+ - Not set → shell disabled, all other tools auto-approved.
767
+ - `recommended` or explicit list → shell enabled, commands gated by
768
+ allow-list; non-shell tools approved unconditionally.
769
+ - `all` → shell enabled, any command allowed, all tools auto-approved.
770
+
771
+ Note: startup header rendering avoids synchronous LangSmith URL lookups.
772
+ A background thread resolves the thread URL concurrently and the result is
773
+ displayed after task completion if available.
774
+
775
+ Args:
776
+ message: The task/message to execute.
777
+ assistant_id: Agent identifier for memory storage.
778
+ model_name: Optional model name to use.
779
+ model_params: Extra kwargs from `--model-params` to pass to the model.
780
+
781
+ These override config file values.
782
+ sandbox_type: Type of sandbox (`'none'`, `'agentcore'`,
783
+ `'daytona'`, `'langsmith'`, `'modal'`, `'runloop'`).
784
+ sandbox_id: Optional existing sandbox ID to reuse.
785
+ sandbox_setup: Optional path to setup script to run in the sandbox
786
+ after creation.
787
+ profile_override: Extra profile fields from `--profile-override`.
788
+
789
+ Merged on top of config file profile overrides.
790
+ quiet: When `True`, all console output (headers, status messages,
791
+ tool notifications, HITL decisions, errors) is redirected to
792
+ stderr so that only the agent's response text appears on stdout.
793
+ stream: When `True` (default), text chunks are written to stdout
794
+ as they arrive.
795
+
796
+ When `False`, the full response is buffered and written to stdout in
797
+ one shot after the agent finishes.
798
+ mcp_config_path: Optional path to MCP servers JSON configuration file.
799
+ Merged on top of auto-discovered configs (highest precedence).
800
+ no_mcp: Disable all MCP tool loading.
801
+ trust_project_mcp: When `True`, allow project-level stdio MCP
802
+ servers. When `False` (default), project stdio servers are
803
+ silently skipped.
804
+
805
+ Returns:
806
+ Exit code: 0 for success, 1 for error, 130 for keyboard interrupt.
807
+ """
808
+ # stderr=True routes all console.print() to stderr; agent response text
809
+ # uses _write_text() -> sys.stdout directly.
810
+ console = Console(stderr=True) if quiet else Console()
811
+ try:
812
+ result = create_model(
813
+ model_name,
814
+ extra_kwargs=model_params,
815
+ profile_overrides=profile_override,
816
+ )
817
+ except ModelConfigError as e:
818
+ console.print(f"[bold red]Error:[/bold red] {e}")
819
+ return 1
820
+
821
+ result.apply_to_settings()
822
+ thread_id = generate_thread_id()
823
+
824
+ from docagent_cli.config import build_stream_config
825
+
826
+ config: RunnableConfig = build_stream_config(
827
+ thread_id, assistant_id, sandbox_type=sandbox_type
828
+ )
829
+
830
+ thread_url_lookup: ThreadUrlLookupState | None = None
831
+ if not quiet:
832
+ thread_url_lookup = _start_langsmith_thread_url_lookup(thread_id)
833
+ console.print(Text("Running task non-interactively...", style="dim"))
834
+ header = _build_non_interactive_header(assistant_id, thread_id)
835
+ console.print(header)
836
+
837
+ import asyncio
838
+
839
+ from docagent_cli.server_manager import server_session
840
+
841
+ # Launch MCP preload concurrently with server startup
842
+ mcp_task: asyncio.Task[Any] | None = None
843
+ if not no_mcp and not quiet:
844
+ try:
845
+ from docagent_cli.main import _preload_session_mcp_server_info
846
+
847
+ mcp_task = asyncio.create_task(
848
+ _preload_session_mcp_server_info(
849
+ mcp_config_path=mcp_config_path,
850
+ no_mcp=no_mcp,
851
+ trust_project_mcp=trust_project_mcp,
852
+ )
853
+ )
854
+ except Exception:
855
+ logger.warning("MCP metadata preload task creation failed", exc_info=True)
856
+
857
+ try:
858
+ enable_shell = bool(settings.shell_allow_list)
859
+ shell_is_unrestricted = isinstance(
860
+ settings.shell_allow_list, type(SHELL_ALLOW_ALL)
861
+ )
862
+ # Currently, non-shell tools have no HITL handler in non-interactive
863
+ # mode, so interrupting on them just fragments LangSmith traces
864
+ # without adding value. Gate only shell execution via middleware.
865
+ use_auto_approve = not enable_shell or shell_is_unrestricted
866
+ use_interrupt_shell_only = enable_shell and not shell_is_unrestricted
867
+ # Extract the concrete allow-list to forward to the server subprocess.
868
+ # settings.shell_allow_list is already validated at this point.
869
+ restrictive_allow_list: list[str] | None = (
870
+ list(settings.shell_allow_list)
871
+ if use_interrupt_shell_only and settings.shell_allow_list
872
+ else None
873
+ )
874
+
875
+ if not quiet:
876
+ console.print(Text("Starting LangGraph server...", style="dim"))
877
+
878
+ async with server_session(
879
+ assistant_id=assistant_id,
880
+ model_name=model_name,
881
+ model_params=model_params,
882
+ auto_approve=use_auto_approve,
883
+ interrupt_shell_only=use_interrupt_shell_only,
884
+ shell_allow_list=restrictive_allow_list,
885
+ sandbox_type=sandbox_type,
886
+ sandbox_id=sandbox_id,
887
+ sandbox_setup=sandbox_setup,
888
+ enable_shell=enable_shell,
889
+ enable_ask_user=False,
890
+ mcp_config_path=mcp_config_path,
891
+ no_mcp=no_mcp,
892
+ trust_project_mcp=trust_project_mcp,
893
+ interactive=False,
894
+ ) as (agent, _server_proc):
895
+ # Collect MCP preload result (ran concurrently with server startup)
896
+ if mcp_task is not None:
897
+ try:
898
+ mcp_info = await mcp_task
899
+ if mcp_info:
900
+ tool_count = sum(len(s.tools) for s in mcp_info)
901
+ if tool_count:
902
+ label = "MCP tool" if tool_count == 1 else "MCP tools"
903
+ console.print(
904
+ f"[green]✓ Loaded {tool_count} {label}[/green]"
905
+ )
906
+ except Exception:
907
+ logger.warning("MCP metadata preload failed", exc_info=True)
908
+
909
+ if not quiet:
910
+ console.print("[green]✓ Server ready[/green]")
911
+
912
+ file_op_tracker = FileOpTracker(assistant_id=assistant_id, backend=None)
913
+
914
+ await _run_agent_loop(
915
+ agent,
916
+ message,
917
+ config,
918
+ console,
919
+ file_op_tracker,
920
+ quiet=quiet,
921
+ stream=stream,
922
+ thread_url_lookup=thread_url_lookup,
923
+ )
924
+
925
+ except KeyboardInterrupt:
926
+ console.print("\n[yellow]Interrupted[/yellow]")
927
+ return 130
928
+ except HITLIterationLimitError as e:
929
+ console.print(f"\n[red]{escape_markup(str(e))}[/red]")
930
+ console.print(
931
+ "[yellow]Hint: The agent may be repeatedly attempting commands "
932
+ "that are not in the allow-list. Consider expanding the "
933
+ "--shell-allow-list or adjusting the task.[/yellow]"
934
+ )
935
+ return 1
936
+ except (ValueError, OSError) as e:
937
+ logger.exception("Error during non-interactive execution")
938
+ console.print(f"\n[red]Error: {escape_markup(str(e))}[/red]")
939
+ return 1
940
+ except Exception as e:
941
+ logger.exception("Unexpected error during non-interactive execution")
942
+ console.print(
943
+ f"\n[red]Unexpected error ({type(e).__name__}): "
944
+ f"{escape_markup(str(e))}[/red]"
945
+ )
946
+ return 1
947
+ else:
948
+ return 0