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,718 @@
1
+ """Middleware for injecting local context into system prompt.
2
+
3
+ Detects git state, project structure, package managers, runtimes, and
4
+ directory layout by running a bash script via the backend. Because the
5
+ script executes inside the backend (local shell or remote sandbox), the
6
+ same detection logic works regardless of where the agent runs.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import asyncio
12
+ import logging
13
+ from typing import (
14
+ TYPE_CHECKING,
15
+ Annotated,
16
+ Any,
17
+ NotRequired,
18
+ Protocol,
19
+ cast,
20
+ runtime_checkable,
21
+ )
22
+
23
+ from langchain.agents.middleware.types import (
24
+ AgentMiddleware,
25
+ AgentState,
26
+ ModelRequest,
27
+ ModelResponse,
28
+ PrivateStateAttr,
29
+ )
30
+
31
+ if TYPE_CHECKING:
32
+ from collections.abc import Awaitable, Callable
33
+
34
+ from deepagents.backends.protocol import ExecuteResponse
35
+ from deepagents.middleware.summarization import SummarizationEvent
36
+ from langgraph.runtime import Runtime
37
+
38
+ from docagent_cli.mcp_tools import MCPServerInfo
39
+
40
+
41
+ _TOOL_NAME_DISPLAY_LIMIT = 10
42
+ """Maximum number of tool names shown per MCP server in the system prompt."""
43
+
44
+ _DETECT_SCRIPT_TIMEOUT = 30
45
+ """Timeout in seconds for the environment detection script."""
46
+
47
+
48
+ def _build_mcp_context(servers: list[MCPServerInfo]) -> str:
49
+ """Format MCP server/tool inventory for the system prompt.
50
+
51
+ Args:
52
+ servers: List of connected MCP server metadata.
53
+
54
+ Returns:
55
+ Formatted markdown string, or `""` if no servers.
56
+ """
57
+ if not servers:
58
+ return ""
59
+
60
+ total_tools = sum(len(s.tools) for s in servers)
61
+ lines = [f"**MCP Servers** ({len(servers)} servers, {total_tools} tools):"]
62
+
63
+ for server in servers:
64
+ if not server.tools:
65
+ lines.append(f"- **{server.name}** ({server.transport}): (no tools)")
66
+ continue
67
+
68
+ names = [t.name for t in server.tools]
69
+ if len(names) > _TOOL_NAME_DISPLAY_LIMIT:
70
+ shown = ", ".join(names[:_TOOL_NAME_DISPLAY_LIMIT])
71
+ remaining = len(names) - _TOOL_NAME_DISPLAY_LIMIT
72
+ lines.append(
73
+ f"- **{server.name}** ({server.transport}): "
74
+ f"{shown}, and {remaining} more"
75
+ )
76
+ else:
77
+ lines.append(
78
+ f"- **{server.name}** ({server.transport}): {', '.join(names)}"
79
+ )
80
+
81
+ return "\n".join(lines)
82
+
83
+
84
+ @runtime_checkable
85
+ class _ExecutableBackend(Protocol):
86
+ """Any backend that supports `execute(command) -> ExecuteResponse`."""
87
+
88
+ def execute(
89
+ self, command: str, *, timeout: int | None = None
90
+ ) -> ExecuteResponse: ...
91
+
92
+
93
+ @runtime_checkable
94
+ class _AsyncExecutableBackend(Protocol):
95
+ """Any backend that provides an async `aexecute` method."""
96
+
97
+ async def aexecute(
98
+ self,
99
+ command: str,
100
+ *,
101
+ timeout: int | None = None, # noqa: ASYNC109 # Timeout is forwarded to backend, not used as asyncio timeout
102
+ ) -> ExecuteResponse: ...
103
+
104
+
105
+ logger = logging.getLogger(__name__)
106
+
107
+ # ---------------------------------------------------------------------------
108
+ # Context detection script
109
+ #
110
+ # Outputs markdown describing the current working environment. Each section
111
+ # is guarded so that missing tools or unsupported environments are silently
112
+ # skipped -- external tools like git, tree, python3, and node are checked
113
+ # with `command -v` before use.
114
+ #
115
+ # The script is built from section functions so each piece can be tested
116
+ # independently. Independent sections run as parallel background subshells;
117
+ # see build_detect_script() for the orchestration logic.
118
+ # ---------------------------------------------------------------------------
119
+
120
+
121
+ def _section_header() -> str:
122
+ """CWD line and IN_GIT flag (used by other sections).
123
+
124
+ Returns:
125
+ Bash snippet that prints the header and sets `CWD` / `IN_GIT`.
126
+ """
127
+ return r"""CWD="$(pwd)"
128
+ echo "## Local Context"
129
+ echo ""
130
+ echo "**Current Directory**: \`${CWD}\`"
131
+ echo ""
132
+
133
+ # --- Check git once ---
134
+ IN_GIT=false
135
+ if command -v git >/dev/null 2>&1 \
136
+ && git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
137
+ IN_GIT=true
138
+ fi"""
139
+
140
+
141
+ def _section_project() -> str:
142
+ """Language, monorepo, git root, virtual-env detection.
143
+
144
+ Returns:
145
+ Bash snippet (requires `CWD` / `IN_GIT` from header).
146
+ """
147
+ return r"""# --- Project ---
148
+ PROJ_LANG=""
149
+ [ -f pyproject.toml ] || [ -f setup.py ] && PROJ_LANG="python"
150
+ [ -z "$PROJ_LANG" ] && [ -f package.json ] && PROJ_LANG="javascript/typescript"
151
+ [ -z "$PROJ_LANG" ] && [ -f Cargo.toml ] && PROJ_LANG="rust"
152
+ [ -z "$PROJ_LANG" ] && [ -f go.mod ] && PROJ_LANG="go"
153
+ [ -z "$PROJ_LANG" ] && { [ -f pom.xml ] || [ -f build.gradle ]; } && PROJ_LANG="java"
154
+
155
+ MONOREPO=false
156
+ { [ -f lerna.json ] || [ -f pnpm-workspace.yaml ] \
157
+ || [ -d packages ] || { [ -d libs ] && [ -d apps ]; } \
158
+ || [ -d workspaces ]; } && MONOREPO=true
159
+
160
+ ROOT=""
161
+ $IN_GIT && ROOT="$(git rev-parse --show-toplevel 2>/dev/null)"
162
+
163
+ ENVS=""
164
+ { [ -d .venv ] || [ -d venv ]; } && ENVS=".venv"
165
+ [ -d node_modules ] && ENVS="${ENVS:+${ENVS}, }node_modules"
166
+
167
+ HAS_PROJECT=false
168
+ { [ -n "$PROJ_LANG" ] || { [ -n "$ROOT" ] && [ "$ROOT" != "$CWD" ]; } \
169
+ || $MONOREPO || [ -n "$ENVS" ]; } && HAS_PROJECT=true
170
+
171
+ if $HAS_PROJECT; then
172
+ echo "**Project**:"
173
+ [ -n "$PROJ_LANG" ] && echo "- Language: ${PROJ_LANG}"
174
+ [ -n "$ROOT" ] && [ "$ROOT" != "$CWD" ] && echo "- Project root: \`${ROOT}\`"
175
+ $MONOREPO && echo "- Monorepo: yes"
176
+ [ -n "$ENVS" ] && echo "- Environments: ${ENVS}"
177
+ echo ""
178
+ fi"""
179
+
180
+
181
+ def _section_package_managers() -> str:
182
+ """Python and Node package manager detection.
183
+
184
+ Returns:
185
+ Bash snippet (standalone).
186
+ """
187
+ return r"""# --- Package managers ---
188
+ PKG=""
189
+ if [ -f uv.lock ]; then PKG="Python: uv"
190
+ elif [ -f poetry.lock ]; then PKG="Python: poetry"
191
+ elif [ -f Pipfile.lock ] || [ -f Pipfile ]; then PKG="Python: pipenv"
192
+ elif [ -f pyproject.toml ]; then
193
+ if grep -q '\[tool\.uv\]' pyproject.toml 2>/dev/null; then PKG="Python: uv"
194
+ elif grep -q '\[tool\.poetry\]' pyproject.toml 2>/dev/null; then PKG="Python: poetry"
195
+ else PKG="Python: pip"
196
+ fi
197
+ elif [ -f requirements.txt ]; then PKG="Python: pip"
198
+ fi
199
+
200
+ NODE_PKG=""
201
+ if [ -f bun.lockb ] || [ -f bun.lock ]; then NODE_PKG="Node: bun"
202
+ elif [ -f pnpm-lock.yaml ]; then NODE_PKG="Node: pnpm"
203
+ elif [ -f yarn.lock ]; then NODE_PKG="Node: yarn"
204
+ elif [ -f package-lock.json ] || [ -f package.json ]; then NODE_PKG="Node: npm"
205
+ fi
206
+ [ -n "$NODE_PKG" ] && PKG="${PKG:+${PKG}, }${NODE_PKG}"
207
+ [ -n "$PKG" ] && echo "**Package Manager**: ${PKG}" && echo ""
208
+ """
209
+
210
+
211
+ def _section_runtimes() -> str:
212
+ """Python and Node runtime version detection.
213
+
214
+ Returns:
215
+ Bash snippet (standalone).
216
+ """
217
+ return r"""# --- Runtimes ---
218
+ RT=""
219
+ if command -v python3 >/dev/null 2>&1; then
220
+ PV="$(python3 --version 2>/dev/null | awk '{print $2}')"
221
+ [ -n "$PV" ] && RT="Python ${PV}"
222
+ fi
223
+ if command -v node >/dev/null 2>&1; then
224
+ NV="$(node --version 2>/dev/null | sed 's/^v//')"
225
+ [ -n "$NV" ] && RT="${RT:+${RT}, }Node ${NV}"
226
+ fi
227
+ [ -n "$RT" ] && echo "**Runtimes**: ${RT}" && echo ""
228
+ """
229
+
230
+
231
+ def _section_git() -> str:
232
+ """Git branch, main branches, uncommitted changes.
233
+
234
+ Returns:
235
+ Bash snippet (requires `IN_GIT` from header).
236
+ """
237
+ return r"""# --- Git ---
238
+ if $IN_GIT; then
239
+ BRANCH="$(git rev-parse --abbrev-ref HEAD 2>/dev/null)"
240
+ GT="**Git**: Current branch \`${BRANCH}\`"
241
+
242
+ MAINS=""
243
+ for b in $(git branch 2>/dev/null | sed 's/^[* ]*//'); do
244
+ case "$b" in
245
+ main) MAINS="${MAINS:+${MAINS}, }\`main\`" ;;
246
+ master) MAINS="${MAINS:+${MAINS}, }\`master\`" ;;
247
+ esac
248
+ done
249
+ [ -n "$MAINS" ] && GT="${GT}, main branch available: ${MAINS}"
250
+
251
+ DC=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ')
252
+ if [ "$DC" -gt 0 ]; then
253
+ if [ "$DC" -eq 1 ]; then GT="${GT}, 1 uncommitted change"
254
+ else GT="${GT}, ${DC} uncommitted changes"
255
+ fi
256
+ fi
257
+
258
+ echo "$GT"
259
+ echo ""
260
+ fi"""
261
+
262
+
263
+ def _section_test_command() -> str:
264
+ """Test command detection (make test / pytest / npm test).
265
+
266
+ Returns:
267
+ Bash snippet (standalone).
268
+ """
269
+ return r"""# --- Test command ---
270
+ TC=""
271
+ if [ -f Makefile ] && grep -qE '^tests?:' Makefile 2>/dev/null; then TC="make test"
272
+ elif [ -f pyproject.toml ]; then
273
+ if grep -q '\[tool\.pytest' pyproject.toml 2>/dev/null \
274
+ || [ -f pytest.ini ] || [ -d tests ] || [ -d test ]; then
275
+ TC="pytest"
276
+ fi
277
+ elif [ -f package.json ] \
278
+ && grep -q '"test"' package.json 2>/dev/null; then
279
+ TC="npm test"
280
+ fi
281
+ [ -n "$TC" ] && echo "**Run Tests**: \`${TC}\`" && echo ""
282
+ """
283
+
284
+
285
+ def _section_files() -> str:
286
+ """Directory listing (filtered, capped at 20).
287
+
288
+ Returns:
289
+ Bash snippet (standalone).
290
+ """
291
+ return r"""# --- Files ---
292
+ EXCL='node_modules|__pycache__|\.pytest_cache'
293
+ EXCL="${EXCL}|\.mypy_cache|\.ruff_cache|\.tox"
294
+ EXCL="${EXCL}|\.coverage|\.eggs|dist|build"
295
+ FILES=$(
296
+ { ls -1 2>/dev/null; [ -e .docagent ] && echo .docagent; } |
297
+ grep -vE "^(${EXCL})$" |
298
+ sort -u
299
+ )
300
+ if [ -n "$FILES" ]; then
301
+ TOTAL=$(echo "$FILES" | wc -l | tr -d ' ')
302
+ SHOWN_FILES=$(echo "$FILES" | head -20)
303
+ SHOWN=$(echo "$SHOWN_FILES" | wc -l | tr -d ' ')
304
+ echo "**Files** (${SHOWN} shown):"
305
+ echo "$SHOWN_FILES" | while IFS= read -r f; do
306
+ if [ -d "$f" ]; then echo "- ${f}/"
307
+ else echo "- ${f}"
308
+ fi
309
+ done
310
+ [ "$SHOWN" -lt "$TOTAL" ] && echo "... ($((TOTAL - SHOWN)) more files)"
311
+ echo ""
312
+ fi"""
313
+
314
+
315
+ def _section_tree() -> str:
316
+ """`tree -L 3` output.
317
+
318
+ Returns:
319
+ Bash snippet (standalone).
320
+ """
321
+ return r"""# --- Tree ---
322
+ if command -v tree >/dev/null 2>&1; then
323
+ TREE_EXCL='node_modules|.venv|__pycache__|.pytest_cache'
324
+ TREE_EXCL="${TREE_EXCL}|.git|.mypy_cache|.ruff_cache"
325
+ TREE_EXCL="${TREE_EXCL}|.tox|.coverage|.eggs|dist|build"
326
+ T=$(tree -L 3 --noreport --dirsfirst \
327
+ -I "$TREE_EXCL" 2>/dev/null | head -22)
328
+ if [ -n "$T" ]; then
329
+ echo "**Tree** (3 levels):"
330
+ echo '```text'
331
+ echo "$T"
332
+ echo '```'
333
+ echo ""
334
+ fi
335
+ fi"""
336
+
337
+
338
+ def _section_makefile() -> str:
339
+ """First 20 lines of Makefile (falls back to git root in monorepos).
340
+
341
+ Returns:
342
+ Bash snippet (requires `ROOT` from `_section_project` and `CWD` from header).
343
+ """
344
+ return r"""# --- Makefile ---
345
+ MK=""
346
+ if [ -f Makefile ]; then
347
+ MK="Makefile"
348
+ elif [ -n "$ROOT" ] && [ "$ROOT" != "$CWD" ] && [ -f "${ROOT}/Makefile" ]; then
349
+ MK="${ROOT}/Makefile"
350
+ fi
351
+ if [ -n "$MK" ]; then
352
+ echo "**Makefile** (\`${MK}\`, first 20 lines):"
353
+ echo '```makefile'
354
+ head -20 "$MK"
355
+ TL=$(wc -l < "$MK" | tr -d ' ')
356
+ [ "$TL" -gt 20 ] && echo "... (truncated)"
357
+ echo '```'
358
+ fi"""
359
+
360
+
361
+ def build_detect_script() -> str:
362
+ """Concatenate all section functions into the full detection script.
363
+
364
+ Independent sections run as parallel background jobs writing to temp
365
+ files, then results are concatenated in the original display order.
366
+ The header (CWD / IN_GIT) and project section (sets ROOT) run first
367
+ because later sections depend on their variables.
368
+
369
+ Returns:
370
+ Complete bash heredoc ready for `backend.execute()`.
371
+ """
372
+ # Header + project run synchronously (set CWD, IN_GIT, ROOT for others)
373
+ serial_prefix = f"{_section_header()}\n{_section_project()}"
374
+
375
+ # These sections are independent — run them in parallel.
376
+ # Subshells inherit parent variables (IN_GIT, ROOT, CWD) via fork.
377
+ # Individual exit codes are not tracked because sections legitimately
378
+ # exit non-zero when they have nothing to report (e.g. no runtimes).
379
+ parallel_sections = [
380
+ ("02_pkgmgr", _section_package_managers()),
381
+ ("03_runtimes", _section_runtimes()),
382
+ ("04_git", _section_git()),
383
+ ("05_testcmd", _section_test_command()),
384
+ ("06_files", _section_files()),
385
+ ("07_tree", _section_tree()),
386
+ ("08_makefile", _section_makefile()),
387
+ ]
388
+
389
+ # Build parallel wrapper: each section runs in a subshell writing to a
390
+ # temp file. Stderr is captured per-section to prevent noise leakage.
391
+ parallel_setup = "_DCT=$(mktemp -d) || exit 1\ntrap 'rm -rf \"$_DCT\"' EXIT"
392
+ parallel_block = "\n".join(
393
+ f'(\n{body}\n) > "$_DCT/{name}" 2>"$_DCT/{name}.err" &'
394
+ for name, body in parallel_sections
395
+ )
396
+ cat_line = "cat " + " ".join(f'"$_DCT/{name}"' for name, _ in parallel_sections)
397
+
398
+ body = f"{serial_prefix}\n{parallel_setup}\n{parallel_block}\nwait\n{cat_line}"
399
+ return f"bash <<'__DETECT_CONTEXT_EOF__'\n{body}\n__DETECT_CONTEXT_EOF__\n"
400
+
401
+
402
+ DETECT_CONTEXT_SCRIPT = build_detect_script()
403
+
404
+ # ---------------------------------------------------------------------------
405
+ # State schema
406
+ # ---------------------------------------------------------------------------
407
+
408
+
409
+ class LocalContextState(AgentState):
410
+ """State for local context middleware."""
411
+
412
+ local_context: NotRequired[str]
413
+ """Formatted local context: cwd, project, package managers,
414
+ runtimes, git, test command, files, tree, Makefile.
415
+ """
416
+
417
+ _local_context_refreshed_at_cutoff: NotRequired[Annotated[int, PrivateStateAttr]]
418
+ """Cutoff index of the summarization event we last refreshed for.
419
+
420
+ Stored in LangGraph checkpointed state (isolated per thread) and private
421
+ (not exposed to subagents via `PrivateStateAttr`). Used to avoid redundant
422
+ re-runs of the detection script for the same summarization event.
423
+ """
424
+
425
+
426
+ # ---------------------------------------------------------------------------
427
+ # Middleware
428
+ # ---------------------------------------------------------------------------
429
+
430
+
431
+ class LocalContextMiddleware(AgentMiddleware):
432
+ """Inject local context (git state, project structure, etc.) into the system prompt.
433
+
434
+ Runs a bash detection script via `backend.execute()` on first interaction
435
+ and again after each summarization event, stores the result in state, and
436
+ appends it to the system prompt on every model call.
437
+
438
+ Because the script runs inside the backend, it works for both local shells
439
+ and remote sandboxes.
440
+ """
441
+
442
+ state_schema = LocalContextState
443
+
444
+ def __init__(
445
+ self,
446
+ backend: _ExecutableBackend | _AsyncExecutableBackend,
447
+ *,
448
+ mcp_server_info: list[MCPServerInfo] | None = None,
449
+ ) -> None:
450
+ """Initialize with a backend that supports shell execution.
451
+
452
+ Args:
453
+ backend: Backend instance that provides shell command execution.
454
+ mcp_server_info: MCP server metadata to include in the system prompt.
455
+ """
456
+ self.backend = backend
457
+ self._mcp_context = _build_mcp_context(mcp_server_info or [])
458
+
459
+ @staticmethod
460
+ def _handle_detect_result(result: ExecuteResponse) -> str | None:
461
+ """Validate detection script output and normalize it for state storage.
462
+
463
+ Args:
464
+ result: Execution result from the backend.
465
+
466
+ Returns:
467
+ Stripped script output, or `None` on failure/empty output.
468
+ """
469
+ output = result.output.strip() if result.output else ""
470
+ if result.exit_code is None or result.exit_code != 0:
471
+ logger.warning(
472
+ "Local context detection script %s; "
473
+ "context will be omitted. Output: %.200s",
474
+ f"exited with code {result.exit_code}"
475
+ if result.exit_code is not None
476
+ else "did not report an exit code",
477
+ output or "(empty)",
478
+ )
479
+ return None
480
+ if not output:
481
+ logger.debug(
482
+ "Local context detection script succeeded but produced no output"
483
+ )
484
+ return output or None
485
+
486
+ def _run_detect_script(self) -> str | None:
487
+ """Run the environment detection script.
488
+
489
+ Returns:
490
+ Stripped script output, or `None` on failure/empty output.
491
+ """
492
+ backend = self.backend
493
+ if not isinstance(backend, _ExecutableBackend):
494
+ logger.debug(
495
+ "Skipping sync local context detection; backend %s only "
496
+ "supports async execution",
497
+ type(backend).__name__,
498
+ )
499
+ return None
500
+ try:
501
+ result = backend.execute(
502
+ DETECT_CONTEXT_SCRIPT, timeout=_DETECT_SCRIPT_TIMEOUT
503
+ )
504
+ except NotImplementedError:
505
+ # Expected for async-only backends (e.g. HarborSandbox) that
506
+ # define a stub execute() raising NotImplementedError.
507
+ logger.debug(
508
+ "Backend %s does not support sync execute; "
509
+ "context detection deferred to async path",
510
+ type(backend).__name__,
511
+ )
512
+ return None
513
+ except Exception:
514
+ logger.warning(
515
+ "Local context detection failed (backend: %s); context will "
516
+ "be omitted from system prompt",
517
+ type(backend).__name__,
518
+ exc_info=True,
519
+ )
520
+ return None
521
+
522
+ return LocalContextMiddleware._handle_detect_result(result)
523
+
524
+ # override - state parameter is intentionally narrowed from
525
+ # AgentState to LocalContextState for type safety within this middleware.
526
+ def before_agent( # type: ignore[override]
527
+ self,
528
+ state: LocalContextState,
529
+ runtime: Runtime, # noqa: ARG002 # Required by interface but not used in local context
530
+ ) -> dict[str, Any] | None:
531
+ """Run context detection on first interaction and refresh after summarization.
532
+
533
+ On the first invocation, runs the detection script and stores the result.
534
+ After a summarization event (indicated by a new `_summarization_event`
535
+ in state), re-runs the script to capture any environment changes that
536
+ occurred during the session.
537
+
538
+ Args:
539
+ state: Current agent state.
540
+ runtime: Runtime context.
541
+
542
+ Returns:
543
+ State update with `local_context` populated on success. On a
544
+ post-summarization refresh failure, returns a state update
545
+ recording the cutoff (without `local_context`) to prevent
546
+ retry loops.
547
+
548
+ Returns `None` if context is already set and no refresh is
549
+ needed, or if initial detection fails.
550
+ """
551
+ # --- Post-summarization refresh ---
552
+ # _summarization_event is a private field from SummarizationState.
553
+ # At runtime the merged state dict contains all middleware fields;
554
+ # accessed as untyped dict value because LocalContextState does not
555
+ # (and should not) redeclare it.
556
+ raw_event = state.get("_summarization_event")
557
+ if raw_event is not None:
558
+ event: SummarizationEvent = raw_event
559
+ cutoff = event.get("cutoff_index")
560
+ refreshed_cutoff = state.get("_local_context_refreshed_at_cutoff")
561
+ if cutoff != refreshed_cutoff:
562
+ output = self._run_detect_script()
563
+ if output:
564
+ return {
565
+ "local_context": output,
566
+ "_local_context_refreshed_at_cutoff": cutoff,
567
+ }
568
+ # Script failed — record cutoff to avoid retry loop,
569
+ # keep existing local_context.
570
+ return {"_local_context_refreshed_at_cutoff": cutoff}
571
+
572
+ # --- Initial detection (first invocation) ---
573
+ if state.get("local_context"):
574
+ return None
575
+
576
+ output = self._run_detect_script()
577
+ if output:
578
+ return {"local_context": output}
579
+ return None
580
+
581
+ async def _arun_detect_script(self) -> str | None:
582
+ """Run the environment detection script asynchronously.
583
+
584
+ Prefers `aexecute` when the backend implements `_AsyncExecutableBackend`.
585
+ Falls back to running the sync detection script in a thread pool
586
+ for sync-only backends.
587
+
588
+ Returns:
589
+ Stripped script output, or `None` on failure/empty output.
590
+ """
591
+ backend = self.backend
592
+ if not (
593
+ isinstance(backend, _AsyncExecutableBackend)
594
+ and asyncio.iscoroutinefunction(backend.aexecute)
595
+ ):
596
+ try:
597
+ return await asyncio.to_thread(self._run_detect_script)
598
+ except Exception:
599
+ logger.warning(
600
+ "Local context detection via sync fallback failed "
601
+ "(backend: %s); context will be omitted from system prompt",
602
+ type(backend).__name__,
603
+ exc_info=True,
604
+ )
605
+ return None
606
+ try:
607
+ result = await backend.aexecute(
608
+ DETECT_CONTEXT_SCRIPT, timeout=_DETECT_SCRIPT_TIMEOUT
609
+ )
610
+ except Exception:
611
+ logger.warning(
612
+ "Local context detection failed (backend: %s); context will "
613
+ "be omitted from system prompt",
614
+ type(backend).__name__,
615
+ exc_info=True,
616
+ )
617
+ return None
618
+
619
+ return LocalContextMiddleware._handle_detect_result(result)
620
+
621
+ async def abefore_agent( # type: ignore[override]
622
+ self,
623
+ state: LocalContextState,
624
+ runtime: Runtime, # noqa: ARG002 # Required by interface but not used in local context
625
+ ) -> dict[str, Any] | None:
626
+ """Async variant of `before_agent` for use in async execution contexts.
627
+
628
+ Args:
629
+ state: Current agent state.
630
+ runtime: Runtime context.
631
+
632
+ Returns:
633
+ State update with `local_context` populated on success. On a
634
+ post-summarization refresh failure, returns a state update
635
+ recording the cutoff (without `local_context`) to prevent
636
+ retry loops.
637
+
638
+ Returns `None` if context is already set and no refresh is
639
+ needed, or if initial detection fails.
640
+ """
641
+ raw_event = state.get("_summarization_event")
642
+ if raw_event is not None:
643
+ event: SummarizationEvent = raw_event
644
+ cutoff = event.get("cutoff_index")
645
+ refreshed_cutoff = state.get("_local_context_refreshed_at_cutoff")
646
+ if cutoff != refreshed_cutoff:
647
+ output = await self._arun_detect_script()
648
+ if output:
649
+ return {
650
+ "local_context": output,
651
+ "_local_context_refreshed_at_cutoff": cutoff,
652
+ }
653
+ return {"_local_context_refreshed_at_cutoff": cutoff}
654
+
655
+ if state.get("local_context"):
656
+ return None
657
+
658
+ output = await self._arun_detect_script()
659
+ if output:
660
+ return {"local_context": output}
661
+ return None
662
+
663
+ def _get_modified_request(self, request: ModelRequest) -> ModelRequest | None:
664
+ """Append local context and MCP info to the system prompt if available.
665
+
666
+ Args:
667
+ request: The model request to potentially modify.
668
+
669
+ Returns:
670
+ Modified request with context appended, or `None`.
671
+ """
672
+ state = cast("LocalContextState", request.state)
673
+ local_context = state.get("local_context", "")
674
+
675
+ parts = [p for p in (local_context, self._mcp_context) if p]
676
+ if not parts:
677
+ return None
678
+
679
+ system_prompt = request.system_prompt or ""
680
+ new_prompt = system_prompt + "\n\n" + "\n\n".join(parts)
681
+ return request.override(system_prompt=new_prompt)
682
+
683
+ def wrap_model_call(
684
+ self,
685
+ request: ModelRequest,
686
+ handler: Callable[[ModelRequest], ModelResponse],
687
+ ) -> ModelResponse:
688
+ """Inject local context into system prompt.
689
+
690
+ Args:
691
+ request: The model request being processed.
692
+ handler: The handler function to call with the modified request.
693
+
694
+ Returns:
695
+ The model response from the handler.
696
+ """
697
+ modified_request = self._get_modified_request(request)
698
+ return handler(modified_request or request)
699
+
700
+ async def awrap_model_call(
701
+ self,
702
+ request: ModelRequest,
703
+ handler: Callable[[ModelRequest], Awaitable[ModelResponse]],
704
+ ) -> ModelResponse:
705
+ """Inject local context into system prompt (async).
706
+
707
+ Args:
708
+ request: The model request being processed.
709
+ handler: The async handler function to call with the modified request.
710
+
711
+ Returns:
712
+ The model response from the handler.
713
+ """
714
+ modified_request = self._get_modified_request(request)
715
+ return await handler(modified_request or request)
716
+
717
+
718
+ __all__ = ["LocalContextMiddleware"]