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.
- docagent_cli/__init__.py +36 -0
- docagent_cli/__main__.py +6 -0
- docagent_cli/_ask_user_types.py +90 -0
- docagent_cli/_cli_context.py +27 -0
- docagent_cli/_debug.py +52 -0
- docagent_cli/_env_vars.py +56 -0
- docagent_cli/_server_config.py +352 -0
- docagent_cli/_session_stats.py +114 -0
- docagent_cli/_testing_models.py +144 -0
- docagent_cli/_version.py +17 -0
- docagent_cli/agent.py +1193 -0
- docagent_cli/app.py +4979 -0
- docagent_cli/app.tcss +283 -0
- docagent_cli/ask_user.py +301 -0
- docagent_cli/built_in_skills/__init__.py +5 -0
- docagent_cli/built_in_skills/doc-coauthoring/SKILL.md +375 -0
- docagent_cli/built_in_skills/docx/LICENSE.txt +30 -0
- docagent_cli/built_in_skills/docx/SKILL.md +590 -0
- docagent_cli/built_in_skills/docx/scripts/__init__.py +1 -0
- docagent_cli/built_in_skills/docx/scripts/accept_changes.py +135 -0
- docagent_cli/built_in_skills/docx/scripts/comment.py +318 -0
- docagent_cli/built_in_skills/docx/scripts/office/helpers/__init__.py +0 -0
- docagent_cli/built_in_skills/docx/scripts/office/helpers/merge_runs.py +199 -0
- docagent_cli/built_in_skills/docx/scripts/office/helpers/simplify_redlines.py +197 -0
- docagent_cli/built_in_skills/docx/scripts/office/pack.py +159 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/mce/mc.xsd +75 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
- docagent_cli/built_in_skills/docx/scripts/office/soffice.py +183 -0
- docagent_cli/built_in_skills/docx/scripts/office/unpack.py +132 -0
- docagent_cli/built_in_skills/docx/scripts/office/validate.py +111 -0
- docagent_cli/built_in_skills/docx/scripts/office/validators/__init__.py +15 -0
- docagent_cli/built_in_skills/docx/scripts/office/validators/base.py +847 -0
- docagent_cli/built_in_skills/docx/scripts/office/validators/docx.py +446 -0
- docagent_cli/built_in_skills/docx/scripts/office/validators/pptx.py +275 -0
- docagent_cli/built_in_skills/docx/scripts/office/validators/redlining.py +247 -0
- docagent_cli/built_in_skills/docx/scripts/templates/comments.xml +3 -0
- docagent_cli/built_in_skills/docx/scripts/templates/commentsExtended.xml +3 -0
- docagent_cli/built_in_skills/docx/scripts/templates/commentsExtensible.xml +3 -0
- docagent_cli/built_in_skills/docx/scripts/templates/commentsIds.xml +3 -0
- docagent_cli/built_in_skills/docx/scripts/templates/people.xml +3 -0
- docagent_cli/built_in_skills/pdf/LICENSE.txt +30 -0
- docagent_cli/built_in_skills/pdf/SKILL.md +314 -0
- docagent_cli/built_in_skills/pdf/forms.md +294 -0
- docagent_cli/built_in_skills/pdf/reference.md +612 -0
- docagent_cli/built_in_skills/pdf/scripts/check_bounding_boxes.py +65 -0
- docagent_cli/built_in_skills/pdf/scripts/check_fillable_fields.py +11 -0
- docagent_cli/built_in_skills/pdf/scripts/convert_pdf_to_images.py +33 -0
- docagent_cli/built_in_skills/pdf/scripts/create_validation_image.py +37 -0
- docagent_cli/built_in_skills/pdf/scripts/extract_form_field_info.py +122 -0
- docagent_cli/built_in_skills/pdf/scripts/extract_form_structure.py +115 -0
- docagent_cli/built_in_skills/pdf/scripts/fill_fillable_fields.py +98 -0
- docagent_cli/built_in_skills/pdf/scripts/fill_pdf_form_with_annotations.py +107 -0
- docagent_cli/built_in_skills/pptx/LICENSE.txt +30 -0
- docagent_cli/built_in_skills/pptx/SKILL.md +232 -0
- docagent_cli/built_in_skills/pptx/editing.md +205 -0
- docagent_cli/built_in_skills/pptx/pptxgenjs.md +420 -0
- docagent_cli/built_in_skills/pptx/scripts/__init__.py +0 -0
- docagent_cli/built_in_skills/pptx/scripts/add_slide.py +195 -0
- docagent_cli/built_in_skills/pptx/scripts/clean.py +286 -0
- docagent_cli/built_in_skills/pptx/scripts/office/helpers/__init__.py +0 -0
- docagent_cli/built_in_skills/pptx/scripts/office/helpers/merge_runs.py +199 -0
- docagent_cli/built_in_skills/pptx/scripts/office/helpers/simplify_redlines.py +197 -0
- docagent_cli/built_in_skills/pptx/scripts/office/pack.py +159 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/mce/mc.xsd +75 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
- docagent_cli/built_in_skills/pptx/scripts/office/soffice.py +183 -0
- docagent_cli/built_in_skills/pptx/scripts/office/unpack.py +132 -0
- docagent_cli/built_in_skills/pptx/scripts/office/validate.py +111 -0
- docagent_cli/built_in_skills/pptx/scripts/office/validators/__init__.py +15 -0
- docagent_cli/built_in_skills/pptx/scripts/office/validators/base.py +847 -0
- docagent_cli/built_in_skills/pptx/scripts/office/validators/docx.py +446 -0
- docagent_cli/built_in_skills/pptx/scripts/office/validators/pptx.py +275 -0
- docagent_cli/built_in_skills/pptx/scripts/office/validators/redlining.py +247 -0
- docagent_cli/built_in_skills/pptx/scripts/thumbnail.py +289 -0
- docagent_cli/built_in_skills/remember/SKILL.md +118 -0
- docagent_cli/built_in_skills/skill-creator/LICENSE.txt +202 -0
- docagent_cli/built_in_skills/skill-creator/SKILL.md +485 -0
- docagent_cli/built_in_skills/skill-creator/agents/analyzer.md +274 -0
- docagent_cli/built_in_skills/skill-creator/agents/comparator.md +202 -0
- docagent_cli/built_in_skills/skill-creator/agents/grader.md +223 -0
- docagent_cli/built_in_skills/skill-creator/assets/eval_review.html +146 -0
- docagent_cli/built_in_skills/skill-creator/eval-viewer/generate_review.py +471 -0
- docagent_cli/built_in_skills/skill-creator/eval-viewer/viewer.html +1325 -0
- docagent_cli/built_in_skills/skill-creator/references/schemas.md +430 -0
- docagent_cli/built_in_skills/skill-creator/scripts/__init__.py +0 -0
- docagent_cli/built_in_skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- docagent_cli/built_in_skills/skill-creator/scripts/generate_report.py +326 -0
- docagent_cli/built_in_skills/skill-creator/scripts/improve_description.py +247 -0
- docagent_cli/built_in_skills/skill-creator/scripts/package_skill.py +136 -0
- docagent_cli/built_in_skills/skill-creator/scripts/quick_validate.py +103 -0
- docagent_cli/built_in_skills/skill-creator/scripts/run_eval.py +310 -0
- docagent_cli/built_in_skills/skill-creator/scripts/run_loop.py +328 -0
- docagent_cli/built_in_skills/skill-creator/scripts/utils.py +47 -0
- docagent_cli/built_in_skills/theme-factory/LICENSE.txt +202 -0
- docagent_cli/built_in_skills/theme-factory/SKILL.md +59 -0
- docagent_cli/built_in_skills/theme-factory/theme-showcase.pdf +0 -0
- docagent_cli/built_in_skills/theme-factory/themes/arctic-frost.md +19 -0
- docagent_cli/built_in_skills/theme-factory/themes/botanical-garden.md +19 -0
- docagent_cli/built_in_skills/theme-factory/themes/desert-rose.md +19 -0
- docagent_cli/built_in_skills/theme-factory/themes/forest-canopy.md +19 -0
- docagent_cli/built_in_skills/theme-factory/themes/golden-hour.md +19 -0
- docagent_cli/built_in_skills/theme-factory/themes/midnight-galaxy.md +19 -0
- docagent_cli/built_in_skills/theme-factory/themes/modern-minimalist.md +19 -0
- docagent_cli/built_in_skills/theme-factory/themes/ocean-depths.md +19 -0
- docagent_cli/built_in_skills/theme-factory/themes/sunset-boulevard.md +19 -0
- docagent_cli/built_in_skills/theme-factory/themes/tech-innovation.md +19 -0
- docagent_cli/built_in_skills/xlsx/LICENSE.txt +30 -0
- docagent_cli/built_in_skills/xlsx/SKILL.md +292 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/helpers/__init__.py +0 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/helpers/merge_runs.py +199 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/helpers/simplify_redlines.py +197 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/pack.py +159 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/mce/mc.xsd +75 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/soffice.py +183 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/unpack.py +132 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/validate.py +111 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/validators/__init__.py +15 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/validators/base.py +847 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/validators/docx.py +446 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/validators/pptx.py +275 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/validators/redlining.py +247 -0
- docagent_cli/built_in_skills/xlsx/scripts/recalc.py +184 -0
- docagent_cli/clipboard.py +128 -0
- docagent_cli/command_registry.py +284 -0
- docagent_cli/config.py +2418 -0
- docagent_cli/configurable_model.py +162 -0
- docagent_cli/default_agent_prompt.md +12 -0
- docagent_cli/editor.py +142 -0
- docagent_cli/file_ops.py +473 -0
- docagent_cli/formatting.py +28 -0
- docagent_cli/hooks.py +206 -0
- docagent_cli/input.py +787 -0
- docagent_cli/integrations/__init__.py +1 -0
- docagent_cli/integrations/sandbox_factory.py +873 -0
- docagent_cli/integrations/sandbox_provider.py +71 -0
- docagent_cli/local_context.py +718 -0
- docagent_cli/main.py +1641 -0
- docagent_cli/mcp_tools.py +707 -0
- docagent_cli/mcp_trust.py +168 -0
- docagent_cli/media_utils.py +478 -0
- docagent_cli/model_config.py +1620 -0
- docagent_cli/non_interactive.py +948 -0
- docagent_cli/offload.py +371 -0
- docagent_cli/output.py +69 -0
- docagent_cli/project_utils.py +188 -0
- docagent_cli/py.typed +0 -0
- docagent_cli/remote_client.py +515 -0
- docagent_cli/server.py +520 -0
- docagent_cli/server_graph.py +196 -0
- docagent_cli/server_manager.py +365 -0
- docagent_cli/sessions.py +1262 -0
- docagent_cli/skills/__init__.py +18 -0
- docagent_cli/skills/commands.py +1090 -0
- docagent_cli/skills/load.py +192 -0
- docagent_cli/subagents.py +173 -0
- docagent_cli/system_prompt.md +247 -0
- docagent_cli/textual_adapter.py +1352 -0
- docagent_cli/theme.py +842 -0
- docagent_cli/token_state.py +31 -0
- docagent_cli/tool_display.py +298 -0
- docagent_cli/tools.py +236 -0
- docagent_cli/ui.py +420 -0
- docagent_cli/unicode_security.py +516 -0
- docagent_cli/update_check.py +454 -0
- docagent_cli/widgets/__init__.py +9 -0
- docagent_cli/widgets/_links.py +63 -0
- docagent_cli/widgets/approval.py +442 -0
- docagent_cli/widgets/ask_user.py +398 -0
- docagent_cli/widgets/autocomplete.py +691 -0
- docagent_cli/widgets/chat_input.py +1827 -0
- docagent_cli/widgets/diff.py +248 -0
- docagent_cli/widgets/history.py +188 -0
- docagent_cli/widgets/loading.py +177 -0
- docagent_cli/widgets/mcp_viewer.py +362 -0
- docagent_cli/widgets/message_store.py +675 -0
- docagent_cli/widgets/messages.py +1751 -0
- docagent_cli/widgets/model_selector.py +964 -0
- docagent_cli/widgets/status.py +372 -0
- docagent_cli/widgets/theme_selector.py +164 -0
- docagent_cli/widgets/thread_selector.py +1905 -0
- docagent_cli/widgets/tool_renderers.py +148 -0
- docagent_cli/widgets/tool_widgets.py +274 -0
- docagent_cli/widgets/welcome.py +339 -0
- docagent_cli-0.0.35.data/data/docagent_cli/default_agent_prompt.md +12 -0
- docagent_cli-0.0.35.dist-info/METADATA +200 -0
- docagent_cli-0.0.35.dist-info/RECORD +300 -0
- docagent_cli-0.0.35.dist-info/WHEEL +4 -0
- docagent_cli-0.0.35.dist-info/entry_points.txt +3 -0
docagent_cli/agent.py
ADDED
|
@@ -0,0 +1,1193 @@
|
|
|
1
|
+
"""Agent management and creation for the CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
import re
|
|
8
|
+
import shutil
|
|
9
|
+
import tempfile
|
|
10
|
+
import tomllib
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import TYPE_CHECKING, Any
|
|
13
|
+
|
|
14
|
+
from deepagents import create_deep_agent
|
|
15
|
+
from deepagents.backends import CompositeBackend, LocalShellBackend
|
|
16
|
+
from deepagents.backends.filesystem import FilesystemBackend
|
|
17
|
+
from deepagents.middleware import MemoryMiddleware, SkillsMiddleware
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from collections.abc import Awaitable, Callable, Sequence
|
|
21
|
+
|
|
22
|
+
from deepagents.backends.sandbox import SandboxBackendProtocol
|
|
23
|
+
from deepagents.middleware.async_subagents import AsyncSubAgent
|
|
24
|
+
from deepagents.middleware.subagents import CompiledSubAgent, SubAgent
|
|
25
|
+
from langchain.agents.middleware import InterruptOnConfig
|
|
26
|
+
from langchain.agents.middleware.types import AgentState
|
|
27
|
+
from langchain.messages import ToolCall
|
|
28
|
+
from langchain.tools import BaseTool
|
|
29
|
+
from langchain_core.language_models import BaseChatModel
|
|
30
|
+
from langchain_core.messages import ToolMessage
|
|
31
|
+
from langgraph.checkpoint.base import BaseCheckpointSaver
|
|
32
|
+
from langgraph.prebuilt.tool_node import ToolCallRequest
|
|
33
|
+
from langgraph.pregel import Pregel
|
|
34
|
+
from langgraph.runtime import Runtime
|
|
35
|
+
from langgraph.types import Command
|
|
36
|
+
|
|
37
|
+
from docagent_cli.mcp_tools import MCPServerInfo
|
|
38
|
+
from docagent_cli.output import OutputFormat
|
|
39
|
+
|
|
40
|
+
from langchain.agents.middleware.types import AgentMiddleware
|
|
41
|
+
|
|
42
|
+
from docagent_cli import theme
|
|
43
|
+
from docagent_cli.config import (
|
|
44
|
+
_ShellAllowAll,
|
|
45
|
+
config,
|
|
46
|
+
console,
|
|
47
|
+
get_default_coding_instructions,
|
|
48
|
+
get_glyphs,
|
|
49
|
+
settings,
|
|
50
|
+
)
|
|
51
|
+
from docagent_cli.configurable_model import ConfigurableModelMiddleware
|
|
52
|
+
from docagent_cli.integrations.sandbox_factory import get_default_working_dir
|
|
53
|
+
from docagent_cli.local_context import (
|
|
54
|
+
LocalContextMiddleware,
|
|
55
|
+
_AsyncExecutableBackend,
|
|
56
|
+
_ExecutableBackend,
|
|
57
|
+
)
|
|
58
|
+
from docagent_cli.project_utils import ProjectContext, get_server_project_context
|
|
59
|
+
from docagent_cli.subagents import list_subagents
|
|
60
|
+
from docagent_cli.unicode_security import (
|
|
61
|
+
check_url_safety,
|
|
62
|
+
detect_dangerous_unicode,
|
|
63
|
+
format_warning_detail,
|
|
64
|
+
render_with_unicode_markers,
|
|
65
|
+
strip_dangerous_unicode,
|
|
66
|
+
summarize_issues,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
logger = logging.getLogger(__name__)
|
|
70
|
+
|
|
71
|
+
DEFAULT_AGENT_NAME = "agent"
|
|
72
|
+
"""The default agent name used when no `-a` flag is provided."""
|
|
73
|
+
|
|
74
|
+
REQUIRE_COMPACT_TOOL_APPROVAL: bool = True
|
|
75
|
+
"""When `True`, `compact_conversation` requires HITL approval like other gated tools."""
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class ShellAllowListMiddleware(AgentMiddleware):
|
|
79
|
+
"""Validate shell commands against an allow-list without HITL interrupts.
|
|
80
|
+
|
|
81
|
+
When the agent invokes a shell tool (any tool in `SHELL_TOOL_NAMES`),
|
|
82
|
+
this middleware checks the command against the configured allow-list
|
|
83
|
+
**before execution**. Rejected commands are returned as error `ToolMessage`
|
|
84
|
+
objects — the graph never pauses, so LangSmith traces stay as a single
|
|
85
|
+
continuous run.
|
|
86
|
+
|
|
87
|
+
Use this middleware in non-interactive mode to avoid the
|
|
88
|
+
interrupt/resume cycle that fragments traces.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
def __init__(self, allow_list: list[str]) -> None:
|
|
92
|
+
"""Initialize with the shell allow-list to validate commands against.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
allow_list: Allowed command names (e.g. `["ls", "cat", "grep"]`).
|
|
96
|
+
Must be a non-empty restrictive list — not `SHELL_ALLOW_ALL`.
|
|
97
|
+
|
|
98
|
+
Raises:
|
|
99
|
+
ValueError: If `allow_list` is empty.
|
|
100
|
+
TypeError: If `allow_list` is the `SHELL_ALLOW_ALL` sentinel.
|
|
101
|
+
"""
|
|
102
|
+
from docagent_cli.config import SHELL_ALLOW_ALL
|
|
103
|
+
|
|
104
|
+
super().__init__()
|
|
105
|
+
if not allow_list:
|
|
106
|
+
msg = "allow_list must not be empty; disable shell access instead"
|
|
107
|
+
raise ValueError(msg)
|
|
108
|
+
if isinstance(allow_list, type(SHELL_ALLOW_ALL)):
|
|
109
|
+
msg = (
|
|
110
|
+
"SHELL_ALLOW_ALL should not be used with "
|
|
111
|
+
"ShellAllowListMiddleware; use auto_approve=True instead"
|
|
112
|
+
)
|
|
113
|
+
raise TypeError(msg)
|
|
114
|
+
self._allow_list = list(allow_list)
|
|
115
|
+
|
|
116
|
+
def _validate_tool_call(self, request: ToolCallRequest) -> ToolMessage | None:
|
|
117
|
+
"""Return an error tool message when a shell command is not allowed.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
request: The tool call request being processed.
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
An error `ToolMessage` when the shell command should be rejected,
|
|
124
|
+
otherwise `None`.
|
|
125
|
+
"""
|
|
126
|
+
from langchain_core.messages import ToolMessage as LCToolMessage
|
|
127
|
+
|
|
128
|
+
from docagent_cli.config import SHELL_TOOL_NAMES, is_shell_command_allowed
|
|
129
|
+
|
|
130
|
+
tool_name = request.tool_call["name"]
|
|
131
|
+
if tool_name not in SHELL_TOOL_NAMES:
|
|
132
|
+
return None
|
|
133
|
+
|
|
134
|
+
args = request.tool_call.get("args") or {}
|
|
135
|
+
command = args.get("command", "")
|
|
136
|
+
if is_shell_command_allowed(command, self._allow_list):
|
|
137
|
+
logger.debug("Shell command allowed: %r", command)
|
|
138
|
+
return None
|
|
139
|
+
|
|
140
|
+
logger.warning("Shell command rejected by allow-list: %r", command)
|
|
141
|
+
allowed_str = ", ".join(self._allow_list)
|
|
142
|
+
return LCToolMessage(
|
|
143
|
+
content=(
|
|
144
|
+
f"Shell command rejected: `{command}` is not in the allow-list. "
|
|
145
|
+
f"Allowed commands: {allowed_str}. "
|
|
146
|
+
f"Please use an allowed command or try another approach."
|
|
147
|
+
),
|
|
148
|
+
name=tool_name,
|
|
149
|
+
tool_call_id=request.tool_call["id"],
|
|
150
|
+
status="error",
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
def wrap_tool_call(
|
|
154
|
+
self,
|
|
155
|
+
request: ToolCallRequest,
|
|
156
|
+
handler: Callable[[ToolCallRequest], ToolMessage | Command[Any]],
|
|
157
|
+
) -> ToolMessage | Command[Any]:
|
|
158
|
+
"""Reject disallowed shell commands; pass everything else through.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
request: The tool call request being processed.
|
|
162
|
+
handler: The next handler in the middleware chain.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
The tool execution result, or an error `ToolMessage` for rejected
|
|
166
|
+
shell commands.
|
|
167
|
+
"""
|
|
168
|
+
if (rejection := self._validate_tool_call(request)) is not None:
|
|
169
|
+
return rejection
|
|
170
|
+
return handler(request)
|
|
171
|
+
|
|
172
|
+
async def awrap_tool_call(
|
|
173
|
+
self,
|
|
174
|
+
request: ToolCallRequest,
|
|
175
|
+
handler: Callable[[ToolCallRequest], Awaitable[ToolMessage | Command[Any]]],
|
|
176
|
+
) -> ToolMessage | Command[Any]:
|
|
177
|
+
"""Reject disallowed shell commands; pass everything else through.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
request: The tool call request being processed.
|
|
181
|
+
handler: The next handler in the middleware chain.
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
The tool execution result, or an error `ToolMessage` for rejected
|
|
185
|
+
shell commands.
|
|
186
|
+
"""
|
|
187
|
+
if (rejection := self._validate_tool_call(request)) is not None:
|
|
188
|
+
return rejection
|
|
189
|
+
return await handler(request)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def load_async_subagents(config_path: Path | None = None) -> list[AsyncSubAgent]:
|
|
193
|
+
"""Load async subagent definitions from `config.toml`.
|
|
194
|
+
|
|
195
|
+
Reads the `[async_subagents]` section where each sub-table defines a remote
|
|
196
|
+
LangGraph deployment:
|
|
197
|
+
|
|
198
|
+
```toml
|
|
199
|
+
[async_subagents.researcher]
|
|
200
|
+
description = "Research agent"
|
|
201
|
+
url = "https://my-deployment.langsmith.dev"
|
|
202
|
+
graph_id = "agent"
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
config_path: Path to config file.
|
|
207
|
+
|
|
208
|
+
Defaults to `~/.docagent/config.toml`.
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
List of `AsyncSubAgent` specs (empty if section is absent or invalid).
|
|
212
|
+
"""
|
|
213
|
+
if config_path is None:
|
|
214
|
+
config_path = Path.home() / ".docagent" / "config.toml"
|
|
215
|
+
|
|
216
|
+
if not config_path.exists():
|
|
217
|
+
return []
|
|
218
|
+
|
|
219
|
+
try:
|
|
220
|
+
with config_path.open("rb") as f:
|
|
221
|
+
data = tomllib.load(f)
|
|
222
|
+
except (tomllib.TOMLDecodeError, PermissionError, OSError) as e:
|
|
223
|
+
logger.warning("Could not read async subagents from %s: %s", config_path, e)
|
|
224
|
+
console.print(
|
|
225
|
+
f"[bold yellow]Warning:[/bold yellow] Could not read async subagents "
|
|
226
|
+
f"from {config_path}: {e}",
|
|
227
|
+
)
|
|
228
|
+
return []
|
|
229
|
+
|
|
230
|
+
section = data.get("async_subagents")
|
|
231
|
+
if not isinstance(section, dict):
|
|
232
|
+
return []
|
|
233
|
+
|
|
234
|
+
required = {"description", "graph_id"}
|
|
235
|
+
agents: list[AsyncSubAgent] = []
|
|
236
|
+
for name, spec in section.items():
|
|
237
|
+
if not isinstance(spec, dict):
|
|
238
|
+
logger.warning("Skipping async subagent '%s': expected a table", name)
|
|
239
|
+
continue
|
|
240
|
+
missing = required - spec.keys()
|
|
241
|
+
if missing:
|
|
242
|
+
logger.warning(
|
|
243
|
+
"Skipping async subagent '%s': missing fields %s", name, missing
|
|
244
|
+
)
|
|
245
|
+
continue
|
|
246
|
+
agent: AsyncSubAgent = {
|
|
247
|
+
"name": name,
|
|
248
|
+
"description": spec["description"],
|
|
249
|
+
"graph_id": spec["graph_id"],
|
|
250
|
+
}
|
|
251
|
+
if "url" in spec and isinstance(spec["url"], str):
|
|
252
|
+
agent["url"] = spec["url"]
|
|
253
|
+
if "headers" in spec and isinstance(spec["headers"], dict):
|
|
254
|
+
agent["headers"] = spec["headers"]
|
|
255
|
+
agents.append(agent)
|
|
256
|
+
|
|
257
|
+
return agents
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def list_agents(*, output_format: OutputFormat = "text") -> None:
|
|
261
|
+
"""List all available agents.
|
|
262
|
+
|
|
263
|
+
Args:
|
|
264
|
+
output_format: Output format — `'text'` (Rich) or `'json'`.
|
|
265
|
+
"""
|
|
266
|
+
agents_dir = settings.user_docagent_dir
|
|
267
|
+
|
|
268
|
+
if not agents_dir.exists() or not any(agents_dir.iterdir()):
|
|
269
|
+
if output_format == "json":
|
|
270
|
+
from docagent_cli.output import write_json
|
|
271
|
+
|
|
272
|
+
write_json("list", [])
|
|
273
|
+
return
|
|
274
|
+
console.print("[yellow]No agents found.[/yellow]")
|
|
275
|
+
console.print(
|
|
276
|
+
"[dim]Agents will be created in ~/.docagent/ "
|
|
277
|
+
"when you first use them.[/dim]",
|
|
278
|
+
style=theme.MUTED,
|
|
279
|
+
)
|
|
280
|
+
return
|
|
281
|
+
|
|
282
|
+
if output_format == "json":
|
|
283
|
+
from docagent_cli.output import write_json
|
|
284
|
+
|
|
285
|
+
agents = []
|
|
286
|
+
for agent_path in sorted(agents_dir.iterdir()):
|
|
287
|
+
if agent_path.is_dir():
|
|
288
|
+
agent_name = agent_path.name
|
|
289
|
+
agents.append(
|
|
290
|
+
{
|
|
291
|
+
"name": agent_name,
|
|
292
|
+
"path": str(agent_path),
|
|
293
|
+
"has_agents_md": (agent_path / "AGENTS.md").exists(),
|
|
294
|
+
"is_default": agent_name == DEFAULT_AGENT_NAME,
|
|
295
|
+
}
|
|
296
|
+
)
|
|
297
|
+
write_json("list", agents)
|
|
298
|
+
return
|
|
299
|
+
|
|
300
|
+
from rich.markup import escape as escape_markup
|
|
301
|
+
|
|
302
|
+
console.print("\n[bold]Available Agents:[/bold]\n", style=theme.PRIMARY)
|
|
303
|
+
|
|
304
|
+
for agent_path in sorted(agents_dir.iterdir()):
|
|
305
|
+
if agent_path.is_dir():
|
|
306
|
+
agent_name = escape_markup(agent_path.name)
|
|
307
|
+
agent_md = agent_path / "AGENTS.md"
|
|
308
|
+
is_default = agent_path.name == DEFAULT_AGENT_NAME
|
|
309
|
+
default_label = " [dim](default)[/dim]" if is_default else ""
|
|
310
|
+
|
|
311
|
+
bullet = get_glyphs().bullet
|
|
312
|
+
if agent_md.exists():
|
|
313
|
+
console.print(
|
|
314
|
+
f" {bullet} [bold]{agent_name}[/bold]{default_label}",
|
|
315
|
+
style=theme.PRIMARY,
|
|
316
|
+
)
|
|
317
|
+
console.print(
|
|
318
|
+
f" {escape_markup(str(agent_path))}",
|
|
319
|
+
style=theme.MUTED,
|
|
320
|
+
)
|
|
321
|
+
else:
|
|
322
|
+
console.print(
|
|
323
|
+
f" {bullet} [bold]{agent_name}[/bold]{default_label}"
|
|
324
|
+
" [dim](incomplete)[/dim]",
|
|
325
|
+
style=theme.WARNING,
|
|
326
|
+
)
|
|
327
|
+
console.print(
|
|
328
|
+
f" {escape_markup(str(agent_path))}",
|
|
329
|
+
style=theme.MUTED,
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
console.print()
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def reset_agent(
|
|
336
|
+
agent_name: str,
|
|
337
|
+
source_agent: str | None = None,
|
|
338
|
+
*,
|
|
339
|
+
dry_run: bool = False,
|
|
340
|
+
output_format: OutputFormat = "text",
|
|
341
|
+
) -> None:
|
|
342
|
+
"""Reset an agent to default or copy from another agent.
|
|
343
|
+
|
|
344
|
+
Args:
|
|
345
|
+
agent_name: Name of the agent to reset.
|
|
346
|
+
source_agent: Copy AGENTS.md from this agent instead of default.
|
|
347
|
+
dry_run: If `True`, print what would happen without making changes.
|
|
348
|
+
output_format: Output format — `'text'` (Rich) or `'json'`.
|
|
349
|
+
|
|
350
|
+
Raises:
|
|
351
|
+
SystemExit: If the source agent is not found.
|
|
352
|
+
"""
|
|
353
|
+
agents_dir = settings.user_docagent_dir
|
|
354
|
+
agent_dir = agents_dir / agent_name
|
|
355
|
+
|
|
356
|
+
if source_agent:
|
|
357
|
+
source_dir = agents_dir / source_agent
|
|
358
|
+
source_md = source_dir / "AGENTS.md"
|
|
359
|
+
|
|
360
|
+
if not source_md.exists():
|
|
361
|
+
console.print(
|
|
362
|
+
f"[bold red]Error:[/bold red] Source agent '{source_agent}' not found "
|
|
363
|
+
"or has no AGENTS.md\n"
|
|
364
|
+
" Available agents: docagent agents list"
|
|
365
|
+
)
|
|
366
|
+
raise SystemExit(1)
|
|
367
|
+
|
|
368
|
+
source_content = source_md.read_text()
|
|
369
|
+
action_desc = f"contents of agent '{source_agent}'"
|
|
370
|
+
else:
|
|
371
|
+
source_content = get_default_coding_instructions()
|
|
372
|
+
action_desc = "default"
|
|
373
|
+
|
|
374
|
+
if dry_run:
|
|
375
|
+
if output_format == "json":
|
|
376
|
+
from docagent_cli.output import write_json
|
|
377
|
+
|
|
378
|
+
write_json(
|
|
379
|
+
"reset",
|
|
380
|
+
{
|
|
381
|
+
"agent": agent_name,
|
|
382
|
+
"reset_to": source_agent or "default",
|
|
383
|
+
"path": str(agent_dir),
|
|
384
|
+
"dry_run": True,
|
|
385
|
+
},
|
|
386
|
+
)
|
|
387
|
+
return
|
|
388
|
+
exists = "remove and recreate" if agent_dir.exists() else "create"
|
|
389
|
+
console.print(f"Would {exists} {agent_dir} with {action_desc} prompt.")
|
|
390
|
+
console.print("No changes made.", style=theme.MUTED)
|
|
391
|
+
return
|
|
392
|
+
|
|
393
|
+
if agent_dir.exists():
|
|
394
|
+
shutil.rmtree(agent_dir)
|
|
395
|
+
if output_format != "json":
|
|
396
|
+
console.print(
|
|
397
|
+
f"Removed existing agent directory: {agent_dir}", style=theme.WARNING
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
agent_dir.mkdir(parents=True, exist_ok=True)
|
|
401
|
+
agent_md = agent_dir / "AGENTS.md"
|
|
402
|
+
agent_md.write_text(source_content)
|
|
403
|
+
|
|
404
|
+
if output_format == "json":
|
|
405
|
+
from docagent_cli.output import write_json
|
|
406
|
+
|
|
407
|
+
write_json(
|
|
408
|
+
"reset",
|
|
409
|
+
{
|
|
410
|
+
"agent": agent_name,
|
|
411
|
+
"reset_to": source_agent or "default",
|
|
412
|
+
"path": str(agent_dir),
|
|
413
|
+
},
|
|
414
|
+
)
|
|
415
|
+
return
|
|
416
|
+
|
|
417
|
+
console.print(
|
|
418
|
+
f"{get_glyphs().checkmark} Agent '{agent_name}' reset to {action_desc}",
|
|
419
|
+
style=theme.PRIMARY,
|
|
420
|
+
)
|
|
421
|
+
console.print(f"Location: {agent_dir}\n", style=theme.MUTED)
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
MODEL_IDENTITY_RE = re.compile(r"### Model Identity\n\n.*?(?=###|\Z)", re.DOTALL)
|
|
425
|
+
"""Matches the `### Model Identity` section in the system prompt, up to the
|
|
426
|
+
next heading or end of string."""
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
def build_model_identity_section(
|
|
430
|
+
name: str | None,
|
|
431
|
+
provider: str | None = None,
|
|
432
|
+
context_limit: int | None = None,
|
|
433
|
+
unsupported_modalities: frozenset[str] = frozenset(),
|
|
434
|
+
) -> str:
|
|
435
|
+
"""Build the `### Model Identity` section for the system prompt.
|
|
436
|
+
|
|
437
|
+
Args:
|
|
438
|
+
name: Model identifier (e.g. `claude-opus-4-6`).
|
|
439
|
+
provider: Provider identifier (e.g. `anthropic`).
|
|
440
|
+
context_limit: Max input tokens from the model profile.
|
|
441
|
+
unsupported_modalities: Input modalities not indicated as supported by
|
|
442
|
+
the model profile (e.g. `{"audio", "video"}`).
|
|
443
|
+
|
|
444
|
+
Returns:
|
|
445
|
+
The section text including the heading and trailing newline,
|
|
446
|
+
or an empty string if `name` is falsy.
|
|
447
|
+
"""
|
|
448
|
+
if not name:
|
|
449
|
+
return ""
|
|
450
|
+
section = f"### Model Identity\n\nYou are running as model `{name}`"
|
|
451
|
+
if provider:
|
|
452
|
+
section += f" (provider: {provider})"
|
|
453
|
+
section += ".\n"
|
|
454
|
+
if context_limit:
|
|
455
|
+
section += f"Your context window is {context_limit:,} tokens.\n"
|
|
456
|
+
if unsupported_modalities:
|
|
457
|
+
items = sorted(unsupported_modalities)
|
|
458
|
+
if len(items) == 1:
|
|
459
|
+
joined = items[0]
|
|
460
|
+
elif len(items) == 2: # noqa: PLR2004
|
|
461
|
+
joined = f"{items[0]} and {items[1]}"
|
|
462
|
+
else:
|
|
463
|
+
joined = ", ".join(items[:-1]) + f", and {items[-1]}"
|
|
464
|
+
section += (
|
|
465
|
+
f"{joined.capitalize()} input may not be available for this model. "
|
|
466
|
+
"Do not attempt to read or process these content types.\n"
|
|
467
|
+
)
|
|
468
|
+
section += "\n"
|
|
469
|
+
return section
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def get_system_prompt(
|
|
473
|
+
assistant_id: str,
|
|
474
|
+
sandbox_type: str | None = None,
|
|
475
|
+
*,
|
|
476
|
+
interactive: bool = True,
|
|
477
|
+
cwd: str | Path | None = None,
|
|
478
|
+
) -> str:
|
|
479
|
+
"""Get the base system prompt for the agent.
|
|
480
|
+
|
|
481
|
+
Loads the base system prompt template from `system_prompt.md` and
|
|
482
|
+
interpolates dynamic sections (model identity, working directory,
|
|
483
|
+
skills path, execution mode).
|
|
484
|
+
|
|
485
|
+
Args:
|
|
486
|
+
assistant_id: The agent identifier for path references
|
|
487
|
+
sandbox_type: Type of sandbox provider
|
|
488
|
+
(`'agentcore'`, `'daytona'`, `'langsmith'`, `'modal'`, `'runloop'`).
|
|
489
|
+
|
|
490
|
+
If `None`, agent is operating in local mode.
|
|
491
|
+
interactive: When `False`, the prompt is tailored for headless
|
|
492
|
+
non-interactive execution (no human in the loop).
|
|
493
|
+
cwd: Override the working directory shown in the prompt.
|
|
494
|
+
|
|
495
|
+
Returns:
|
|
496
|
+
The system prompt string
|
|
497
|
+
|
|
498
|
+
Example:
|
|
499
|
+
```txt
|
|
500
|
+
You are running as model {MODEL} (provider: {PROVIDER}).
|
|
501
|
+
|
|
502
|
+
Your context window is {CONTEXT_WINDOW} tokens.
|
|
503
|
+
|
|
504
|
+
... {CONDITIONAL SECTIONS} ...
|
|
505
|
+
```
|
|
506
|
+
"""
|
|
507
|
+
template = (Path(__file__).parent / "system_prompt.md").read_text()
|
|
508
|
+
|
|
509
|
+
skills_path = f"~/.docagent/{assistant_id}/skills"
|
|
510
|
+
|
|
511
|
+
if interactive:
|
|
512
|
+
mode_description = "an interactive CLI on the user's computer"
|
|
513
|
+
interactive_preamble = (
|
|
514
|
+
"The user sends you messages and you respond with text and tool "
|
|
515
|
+
"calls. Your tools run on the user's machine. The user can see "
|
|
516
|
+
"your responses and tool outputs in real time, so keep them "
|
|
517
|
+
"informed — but don't over-explain."
|
|
518
|
+
)
|
|
519
|
+
ambiguity_guidance = (
|
|
520
|
+
"- If the request is ambiguous, ask questions before acting.\n"
|
|
521
|
+
"- If asked how to approach something, explain first, then act."
|
|
522
|
+
)
|
|
523
|
+
else:
|
|
524
|
+
mode_description = (
|
|
525
|
+
"non-interactive (headless) mode — there is no human operator "
|
|
526
|
+
"monitoring your output in real time"
|
|
527
|
+
)
|
|
528
|
+
interactive_preamble = (
|
|
529
|
+
"You received a single task and must complete it fully and "
|
|
530
|
+
"autonomously. There is no human available to answer follow-up "
|
|
531
|
+
"questions, so do NOT ask for clarification — make reasonable "
|
|
532
|
+
"assumptions and proceed."
|
|
533
|
+
)
|
|
534
|
+
ambiguity_guidance = (
|
|
535
|
+
"- Do NOT ask clarifying questions — there is no human to answer "
|
|
536
|
+
"them. Make reasonable assumptions and proceed.\n"
|
|
537
|
+
"- If you encounter ambiguity, choose the most reasonable "
|
|
538
|
+
"interpretation and note your assumption briefly.\n"
|
|
539
|
+
"- Always use non-interactive command variants — no human is "
|
|
540
|
+
"available to respond to prompts. Examples: `npm init -y` not "
|
|
541
|
+
"`npm init`, `apt-get install -y` not `apt-get install`, "
|
|
542
|
+
"`yes |` or `--no-input`/`--non-interactive` flags where "
|
|
543
|
+
"available. Never run commands that block waiting for stdin."
|
|
544
|
+
)
|
|
545
|
+
|
|
546
|
+
model_identity_section = build_model_identity_section(
|
|
547
|
+
settings.model_name,
|
|
548
|
+
provider=settings.model_provider,
|
|
549
|
+
context_limit=settings.model_context_limit,
|
|
550
|
+
unsupported_modalities=settings.model_unsupported_modalities,
|
|
551
|
+
)
|
|
552
|
+
|
|
553
|
+
# Build working directory section (local vs sandbox)
|
|
554
|
+
if sandbox_type:
|
|
555
|
+
working_dir = get_default_working_dir(sandbox_type)
|
|
556
|
+
working_dir_section = (
|
|
557
|
+
f"### Current Working Directory\n\n"
|
|
558
|
+
f"You are operating in a **remote Linux sandbox** at `{working_dir}`.\n\n"
|
|
559
|
+
f"All code execution and file operations happen in this sandbox "
|
|
560
|
+
f"environment.\n\n"
|
|
561
|
+
f"**Important:**\n"
|
|
562
|
+
f"- The CLI is running locally on the user's machine, but you execute "
|
|
563
|
+
f"code remotely\n"
|
|
564
|
+
f"- Use `{working_dir}` as your working directory for all operations\n"
|
|
565
|
+
f"- **You do NOT have access to the user's local filesystem.** Paths "
|
|
566
|
+
f"like `/Users/...`, `/home/<local-user>/...`, `C:\\...`, etc. do not "
|
|
567
|
+
f"exist in this sandbox. Never reference or attempt to read/write local "
|
|
568
|
+
f"paths — all files must be within the sandbox at `{working_dir}`\n"
|
|
569
|
+
f"- When delegating to subagents, ensure they also use sandbox paths "
|
|
570
|
+
f"(`{working_dir}/...`), not local paths\n\n"
|
|
571
|
+
)
|
|
572
|
+
else:
|
|
573
|
+
if cwd is not None:
|
|
574
|
+
resolved_cwd = Path(cwd)
|
|
575
|
+
else:
|
|
576
|
+
try:
|
|
577
|
+
resolved_cwd = Path.cwd()
|
|
578
|
+
except OSError:
|
|
579
|
+
logger.warning(
|
|
580
|
+
"Could not determine working directory for system prompt",
|
|
581
|
+
exc_info=True,
|
|
582
|
+
)
|
|
583
|
+
resolved_cwd = Path()
|
|
584
|
+
cwd = resolved_cwd
|
|
585
|
+
working_dir_section = (
|
|
586
|
+
f"### Current Working Directory\n\n"
|
|
587
|
+
f"The filesystem backend is currently operating in: `{cwd}`\n\n"
|
|
588
|
+
f"### File System and Paths\n\n"
|
|
589
|
+
f"**IMPORTANT - Path Handling:**\n"
|
|
590
|
+
f"- All file paths must be absolute paths (e.g., `{cwd}/file.txt`)\n"
|
|
591
|
+
f"- Use the working directory to construct absolute paths\n"
|
|
592
|
+
f"- Example: To create a file in your working directory, "
|
|
593
|
+
f"use `{cwd}/research_project/file.md`\n"
|
|
594
|
+
f"- Never use relative paths - always construct full absolute paths\n\n"
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
result = (
|
|
598
|
+
template.replace("{mode_description}", mode_description)
|
|
599
|
+
.replace("{interactive_preamble}", interactive_preamble)
|
|
600
|
+
.replace("{ambiguity_guidance}", ambiguity_guidance)
|
|
601
|
+
.replace("{model_identity_section}", model_identity_section)
|
|
602
|
+
.replace("{working_dir_section}", working_dir_section)
|
|
603
|
+
.replace("{skills_path}", skills_path)
|
|
604
|
+
)
|
|
605
|
+
|
|
606
|
+
# Detect unreplaced placeholders (defense-in-depth for template typos)
|
|
607
|
+
unreplaced = re.findall(r"\{[a-z_]+\}", result)
|
|
608
|
+
if unreplaced:
|
|
609
|
+
logger.warning("System prompt contains unreplaced placeholders: %s", unreplaced)
|
|
610
|
+
|
|
611
|
+
return result
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
def _format_write_file_description(
|
|
615
|
+
tool_call: ToolCall, _state: AgentState[Any], _runtime: Runtime[Any]
|
|
616
|
+
) -> str:
|
|
617
|
+
"""Format write_file tool call for approval prompt.
|
|
618
|
+
|
|
619
|
+
Returns:
|
|
620
|
+
Formatted description string for the write_file tool call.
|
|
621
|
+
"""
|
|
622
|
+
args = tool_call["args"]
|
|
623
|
+
file_path = args.get("file_path", "unknown")
|
|
624
|
+
|
|
625
|
+
action = "Overwrite" if Path(file_path).exists() else "Create"
|
|
626
|
+
|
|
627
|
+
return f"Action: {action} file"
|
|
628
|
+
|
|
629
|
+
|
|
630
|
+
def _format_edit_file_description(
|
|
631
|
+
tool_call: ToolCall, _state: AgentState[Any], _runtime: Runtime[Any]
|
|
632
|
+
) -> str:
|
|
633
|
+
"""Format edit_file tool call for approval prompt.
|
|
634
|
+
|
|
635
|
+
Returns:
|
|
636
|
+
Formatted description string for the edit_file tool call.
|
|
637
|
+
"""
|
|
638
|
+
args = tool_call["args"]
|
|
639
|
+
replace_all = bool(args.get("replace_all", False))
|
|
640
|
+
|
|
641
|
+
scope = "all occurrences" if replace_all else "single occurrence"
|
|
642
|
+
return f"Action: Replace text ({scope})"
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
def _format_web_search_description(
|
|
646
|
+
tool_call: ToolCall, _state: AgentState[Any], _runtime: Runtime[Any]
|
|
647
|
+
) -> str:
|
|
648
|
+
"""Format web_search tool call for approval prompt.
|
|
649
|
+
|
|
650
|
+
Returns:
|
|
651
|
+
Formatted description string for the web_search tool call.
|
|
652
|
+
"""
|
|
653
|
+
args = tool_call["args"]
|
|
654
|
+
query = args.get("query", "unknown")
|
|
655
|
+
max_results = args.get("max_results", 5)
|
|
656
|
+
|
|
657
|
+
return (
|
|
658
|
+
f"Query: {query}\nMax results: {max_results}\n\n"
|
|
659
|
+
f"{get_glyphs().warning} This will use Tavily API credits"
|
|
660
|
+
)
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
def _format_fetch_url_description(
|
|
664
|
+
tool_call: ToolCall, _state: AgentState[Any], _runtime: Runtime[Any]
|
|
665
|
+
) -> str:
|
|
666
|
+
"""Format fetch_url tool call for approval prompt.
|
|
667
|
+
|
|
668
|
+
Returns:
|
|
669
|
+
Formatted description string for the fetch_url tool call.
|
|
670
|
+
"""
|
|
671
|
+
args = tool_call["args"]
|
|
672
|
+
url = str(args.get("url", "unknown"))
|
|
673
|
+
display_url = strip_dangerous_unicode(url)
|
|
674
|
+
timeout = args.get("timeout", 30)
|
|
675
|
+
safety = check_url_safety(url)
|
|
676
|
+
|
|
677
|
+
warning_lines: list[str] = []
|
|
678
|
+
if not safety.safe:
|
|
679
|
+
detail = format_warning_detail(safety.warnings)
|
|
680
|
+
warning_lines.append(f"{get_glyphs().warning} URL warning: {detail}")
|
|
681
|
+
if safety.decoded_domain:
|
|
682
|
+
warning_lines.append(
|
|
683
|
+
f"{get_glyphs().warning} Decoded domain: {safety.decoded_domain}"
|
|
684
|
+
)
|
|
685
|
+
|
|
686
|
+
warning_block = "\n".join(warning_lines)
|
|
687
|
+
if warning_block:
|
|
688
|
+
warning_block = f"\n{warning_block}"
|
|
689
|
+
|
|
690
|
+
return (
|
|
691
|
+
f"URL: {display_url}\nTimeout: {timeout}s\n\n"
|
|
692
|
+
f"{get_glyphs().warning} Will fetch and convert web content to markdown"
|
|
693
|
+
f"{warning_block}"
|
|
694
|
+
)
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
def _format_task_description(
|
|
698
|
+
tool_call: ToolCall, _state: AgentState[Any], _runtime: Runtime[Any]
|
|
699
|
+
) -> str:
|
|
700
|
+
"""Format task (subagent) tool call for approval prompt.
|
|
701
|
+
|
|
702
|
+
The task tool signature is: task(description: str, subagent_type: str)
|
|
703
|
+
The description contains all instructions that will be sent to the subagent.
|
|
704
|
+
|
|
705
|
+
Returns:
|
|
706
|
+
Formatted description string for the task tool call.
|
|
707
|
+
"""
|
|
708
|
+
args = tool_call["args"]
|
|
709
|
+
description = args.get("description", "unknown")
|
|
710
|
+
subagent_type = args.get("subagent_type", "unknown")
|
|
711
|
+
|
|
712
|
+
# Truncate description if too long for display
|
|
713
|
+
description_preview = description
|
|
714
|
+
if len(description) > 500: # noqa: PLR2004 # Subagent description length threshold
|
|
715
|
+
description_preview = description[:500] + "..."
|
|
716
|
+
|
|
717
|
+
glyphs = get_glyphs()
|
|
718
|
+
separator = glyphs.box_horizontal * 40
|
|
719
|
+
warning_msg = "Subagent will have access to file operations and shell commands"
|
|
720
|
+
return (
|
|
721
|
+
f"Subagent Type: {subagent_type}\n\n"
|
|
722
|
+
f"{glyphs.warning} {warning_msg} {glyphs.warning}\n\n"
|
|
723
|
+
f"Task Instructions:\n"
|
|
724
|
+
f"{separator}\n"
|
|
725
|
+
f"{description_preview}"
|
|
726
|
+
)
|
|
727
|
+
|
|
728
|
+
|
|
729
|
+
def _format_execute_description(
|
|
730
|
+
tool_call: ToolCall, _state: AgentState[Any], _runtime: Runtime[Any]
|
|
731
|
+
) -> str:
|
|
732
|
+
"""Format execute tool call for approval prompt.
|
|
733
|
+
|
|
734
|
+
Returns:
|
|
735
|
+
Formatted description string for the execute tool call.
|
|
736
|
+
"""
|
|
737
|
+
args = tool_call["args"]
|
|
738
|
+
command_raw = str(args.get("command", "N/A"))
|
|
739
|
+
command = strip_dangerous_unicode(command_raw)
|
|
740
|
+
project_context = get_server_project_context()
|
|
741
|
+
effective_cwd = (
|
|
742
|
+
str(project_context.user_cwd)
|
|
743
|
+
if project_context is not None
|
|
744
|
+
else str(Path.cwd())
|
|
745
|
+
)
|
|
746
|
+
lines = [f"Execute Command: {command}", f"Working Directory: {effective_cwd}"]
|
|
747
|
+
|
|
748
|
+
issues = detect_dangerous_unicode(command_raw)
|
|
749
|
+
if issues:
|
|
750
|
+
summary = summarize_issues(issues)
|
|
751
|
+
lines.append(f"{get_glyphs().warning} Hidden Unicode detected: {summary}")
|
|
752
|
+
raw_marked = render_with_unicode_markers(command_raw)
|
|
753
|
+
if len(raw_marked) > 220: # noqa: PLR2004 # UI display truncation threshold
|
|
754
|
+
raw_marked = raw_marked[:220] + "..."
|
|
755
|
+
lines.append(f"Raw: {raw_marked}")
|
|
756
|
+
|
|
757
|
+
return "\n".join(lines)
|
|
758
|
+
|
|
759
|
+
|
|
760
|
+
def _add_interrupt_on() -> dict[str, InterruptOnConfig]:
|
|
761
|
+
"""Configure human-in-the-loop interrupt settings for all gated tools.
|
|
762
|
+
|
|
763
|
+
Every tool that can have side effects or access external resources
|
|
764
|
+
(shell execution, file writes/edits, web search, URL fetch, task
|
|
765
|
+
delegation) is gated behind an approval prompt unless auto-approve
|
|
766
|
+
is enabled.
|
|
767
|
+
|
|
768
|
+
Returns:
|
|
769
|
+
Dictionary mapping tool names to their interrupt configuration.
|
|
770
|
+
"""
|
|
771
|
+
execute_interrupt_config: InterruptOnConfig = {
|
|
772
|
+
"allowed_decisions": ["approve", "reject"],
|
|
773
|
+
"description": _format_execute_description, # type: ignore[typeddict-item] # Callable description narrower than TypedDict expects
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
write_file_interrupt_config: InterruptOnConfig = {
|
|
777
|
+
"allowed_decisions": ["approve", "reject"],
|
|
778
|
+
"description": _format_write_file_description, # type: ignore[typeddict-item] # Callable description narrower than TypedDict expects
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
edit_file_interrupt_config: InterruptOnConfig = {
|
|
782
|
+
"allowed_decisions": ["approve", "reject"],
|
|
783
|
+
"description": _format_edit_file_description, # type: ignore[typeddict-item] # Callable description narrower than TypedDict expects
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
web_search_interrupt_config: InterruptOnConfig = {
|
|
787
|
+
"allowed_decisions": ["approve", "reject"],
|
|
788
|
+
"description": _format_web_search_description, # type: ignore[typeddict-item] # Callable description narrower than TypedDict expects
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
fetch_url_interrupt_config: InterruptOnConfig = {
|
|
792
|
+
"allowed_decisions": ["approve", "reject"],
|
|
793
|
+
"description": _format_fetch_url_description, # type: ignore[typeddict-item] # Callable description narrower than TypedDict expects
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
task_interrupt_config: InterruptOnConfig = {
|
|
797
|
+
"allowed_decisions": ["approve", "reject"],
|
|
798
|
+
"description": _format_task_description, # type: ignore[typeddict-item] # Callable description narrower than TypedDict expects
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
async_subagent_interrupt_config: InterruptOnConfig = {
|
|
802
|
+
"allowed_decisions": ["approve", "reject"],
|
|
803
|
+
"description": "Launch, update, or cancel a remote async subagent.",
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
interrupt_map: dict[str, InterruptOnConfig] = {
|
|
807
|
+
"execute": execute_interrupt_config,
|
|
808
|
+
"write_file": write_file_interrupt_config,
|
|
809
|
+
"edit_file": edit_file_interrupt_config,
|
|
810
|
+
"web_search": web_search_interrupt_config,
|
|
811
|
+
"fetch_url": fetch_url_interrupt_config,
|
|
812
|
+
"task": task_interrupt_config,
|
|
813
|
+
"launch_async_subagent": async_subagent_interrupt_config,
|
|
814
|
+
"update_async_subagent": async_subagent_interrupt_config,
|
|
815
|
+
"cancel_async_subagent": async_subagent_interrupt_config,
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
if REQUIRE_COMPACT_TOOL_APPROVAL:
|
|
819
|
+
interrupt_map["compact_conversation"] = {
|
|
820
|
+
"allowed_decisions": ["approve", "reject"],
|
|
821
|
+
"description": (
|
|
822
|
+
"Offloads older messages to backend storage and "
|
|
823
|
+
"replaces them with a summary, freeing context "
|
|
824
|
+
"window space. Recent messages are kept as-is. "
|
|
825
|
+
"Full history remains available for retrieval."
|
|
826
|
+
),
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
return interrupt_map
|
|
830
|
+
|
|
831
|
+
|
|
832
|
+
def create_cli_agent(
|
|
833
|
+
model: str | BaseChatModel,
|
|
834
|
+
assistant_id: str,
|
|
835
|
+
*,
|
|
836
|
+
tools: Sequence[BaseTool | Callable | dict[str, Any]] | None = None,
|
|
837
|
+
sandbox: SandboxBackendProtocol | None = None,
|
|
838
|
+
sandbox_type: str | None = None,
|
|
839
|
+
system_prompt: str | None = None,
|
|
840
|
+
interactive: bool = True,
|
|
841
|
+
auto_approve: bool = False,
|
|
842
|
+
interrupt_shell_only: bool = False,
|
|
843
|
+
shell_allow_list: list[str] | None = None,
|
|
844
|
+
enable_ask_user: bool = True,
|
|
845
|
+
enable_memory: bool = True,
|
|
846
|
+
enable_skills: bool = True,
|
|
847
|
+
enable_shell: bool = True,
|
|
848
|
+
checkpointer: BaseCheckpointSaver | None = None,
|
|
849
|
+
mcp_server_info: list[MCPServerInfo] | None = None,
|
|
850
|
+
cwd: str | Path | None = None,
|
|
851
|
+
project_context: ProjectContext | None = None,
|
|
852
|
+
async_subagents: list[AsyncSubAgent] | None = None,
|
|
853
|
+
) -> tuple[Pregel, CompositeBackend]:
|
|
854
|
+
"""Create a CLI-configured agent with flexible options.
|
|
855
|
+
|
|
856
|
+
This is the main entry point for creating a docagent CLI agent, usable
|
|
857
|
+
both internally and from external code (e.g., benchmarking frameworks).
|
|
858
|
+
|
|
859
|
+
Args:
|
|
860
|
+
model: LLM model to use (e.g., `'anthropic:claude-sonnet-4-6'`)
|
|
861
|
+
assistant_id: Agent identifier for memory/state storage
|
|
862
|
+
tools: Additional tools to provide to agent
|
|
863
|
+
sandbox: Optional sandbox backend for remote execution
|
|
864
|
+
(e.g., `ModalSandbox`).
|
|
865
|
+
|
|
866
|
+
If `None`, uses local filesystem + shell.
|
|
867
|
+
sandbox_type: Type of sandbox provider
|
|
868
|
+
(`'agentcore'`, `'daytona'`, `'langsmith'`, `'modal'`, `'runloop'`).
|
|
869
|
+
Used for system prompt generation.
|
|
870
|
+
system_prompt: Override the default system prompt.
|
|
871
|
+
|
|
872
|
+
If `None`, generates one based on `sandbox_type`, `assistant_id`,
|
|
873
|
+
and `interactive`.
|
|
874
|
+
interactive: When `False`, the auto-generated system prompt is
|
|
875
|
+
tailored for headless non-interactive execution. Ignored when
|
|
876
|
+
`system_prompt` is provided explicitly.
|
|
877
|
+
auto_approve: If `True`, no tools trigger human-in-the-loop
|
|
878
|
+
interrupts — all calls (shell execution, file writes/edits,
|
|
879
|
+
web search, URL fetch) run automatically.
|
|
880
|
+
|
|
881
|
+
If `False`, tools pause for user confirmation via the approval menu.
|
|
882
|
+
See `_add_interrupt_on` for the full list of gated tools.
|
|
883
|
+
interrupt_shell_only: If `True`, all HITL interrupts are disabled;
|
|
884
|
+
shell commands are validated inline by `ShellAllowListMiddleware`
|
|
885
|
+
against the configured allow-list instead.
|
|
886
|
+
|
|
887
|
+
Used in non-interactive mode with a restrictive shell allow-list
|
|
888
|
+
to avoid splitting traces into multiple LangSmith runs.
|
|
889
|
+
|
|
890
|
+
Has no effect when `auto_approve` is `True` (interrupts are already
|
|
891
|
+
disabled) or when `shell_allow_list` is `SHELL_ALLOW_ALL`.
|
|
892
|
+
shell_allow_list: Explicit restrictive shell allow-list forwarded from
|
|
893
|
+
the CLI process. When provided (and `interrupt_shell_only` is
|
|
894
|
+
`True`), used directly instead of reading `settings.shell_allow_list`
|
|
895
|
+
(which may not be set in the server subprocess environment).
|
|
896
|
+
enable_ask_user: Enable `AskUserMiddleware` so the agent can ask
|
|
897
|
+
clarifying questions.
|
|
898
|
+
|
|
899
|
+
Disabled in non-interactive mode.
|
|
900
|
+
enable_memory: Enable `MemoryMiddleware` for persistent memory
|
|
901
|
+
enable_skills: Enable `SkillsMiddleware` for custom agent skills
|
|
902
|
+
enable_shell: Enable shell execution via `LocalShellBackend`
|
|
903
|
+
(only in local mode). When enabled, the `execute` tool is available.
|
|
904
|
+
checkpointer: Optional checkpointer for session persistence.
|
|
905
|
+
When `None`, the graph is compiled without a checkpointer.
|
|
906
|
+
mcp_server_info: MCP server metadata to surface in the system prompt.
|
|
907
|
+
cwd: Override the working directory for the agent's filesystem backend
|
|
908
|
+
and system prompt.
|
|
909
|
+
project_context: Explicit project path context for project-sensitive
|
|
910
|
+
behavior such as project `AGENTS.md` files, skills, subagents, and
|
|
911
|
+
MCP trust.
|
|
912
|
+
async_subagents: Remote LangGraph deployments to expose as async subagent tools.
|
|
913
|
+
|
|
914
|
+
Loaded from `[async_subagents]` in `config.toml` or passed directly.
|
|
915
|
+
|
|
916
|
+
Returns:
|
|
917
|
+
2-tuple of `(agent_graph, backend)`
|
|
918
|
+
|
|
919
|
+
- `agent_graph`: Configured LangGraph Pregel instance ready
|
|
920
|
+
for execution
|
|
921
|
+
- `composite_backend`: `CompositeBackend` for file operations
|
|
922
|
+
"""
|
|
923
|
+
tools = tools or []
|
|
924
|
+
effective_cwd = (
|
|
925
|
+
Path(cwd)
|
|
926
|
+
if cwd is not None
|
|
927
|
+
else (project_context.user_cwd if project_context is not None else None)
|
|
928
|
+
)
|
|
929
|
+
|
|
930
|
+
# Setup agent directory for persistent memory (if enabled)
|
|
931
|
+
if enable_memory or enable_skills:
|
|
932
|
+
agent_dir = settings.ensure_agent_dir(assistant_id)
|
|
933
|
+
agent_md = agent_dir / "AGENTS.md"
|
|
934
|
+
if not agent_md.exists():
|
|
935
|
+
# Create empty file for user customizations
|
|
936
|
+
# Base instructions are loaded fresh from get_system_prompt()
|
|
937
|
+
agent_md.touch()
|
|
938
|
+
|
|
939
|
+
# Skills directories (if enabled)
|
|
940
|
+
skills_dir = None
|
|
941
|
+
user_agent_skills_dir = None
|
|
942
|
+
project_skills_dir = None
|
|
943
|
+
project_agent_skills_dir = None
|
|
944
|
+
if enable_skills:
|
|
945
|
+
skills_dir = settings.ensure_user_skills_dir(assistant_id)
|
|
946
|
+
user_agent_skills_dir = settings.get_user_agent_skills_dir()
|
|
947
|
+
project_skills_dir = (
|
|
948
|
+
project_context.project_skills_dir()
|
|
949
|
+
if project_context is not None
|
|
950
|
+
else settings.get_project_skills_dir()
|
|
951
|
+
)
|
|
952
|
+
project_agent_skills_dir = (
|
|
953
|
+
project_context.project_agent_skills_dir()
|
|
954
|
+
if project_context is not None
|
|
955
|
+
else settings.get_project_agent_skills_dir()
|
|
956
|
+
)
|
|
957
|
+
|
|
958
|
+
# Load custom subagents from filesystem
|
|
959
|
+
custom_subagents: list[SubAgent | CompiledSubAgent] = []
|
|
960
|
+
restrictive_shell_allow_list: list[str] | None = None
|
|
961
|
+
if interrupt_shell_only and not auto_approve:
|
|
962
|
+
# Prefer the explicitly forwarded allow-list (set by the CLI process
|
|
963
|
+
# and passed through ServerConfig). Fall back to settings only for
|
|
964
|
+
# direct callers (e.g. benchmarking frameworks) that don't go through
|
|
965
|
+
# the server subprocess path.
|
|
966
|
+
if shell_allow_list:
|
|
967
|
+
restrictive_shell_allow_list = list(shell_allow_list)
|
|
968
|
+
elif settings.shell_allow_list and not isinstance(
|
|
969
|
+
settings.shell_allow_list, _ShellAllowAll
|
|
970
|
+
):
|
|
971
|
+
restrictive_shell_allow_list = list(settings.shell_allow_list)
|
|
972
|
+
else:
|
|
973
|
+
logger.warning(
|
|
974
|
+
"interrupt_shell_only=True but no restrictive shell allow-list "
|
|
975
|
+
"available; falling back to standard HITL interrupts"
|
|
976
|
+
)
|
|
977
|
+
|
|
978
|
+
user_agents_dir = settings.get_user_agents_dir(assistant_id)
|
|
979
|
+
project_agents_dir = (
|
|
980
|
+
project_context.project_agents_dir()
|
|
981
|
+
if project_context is not None
|
|
982
|
+
else settings.get_project_agents_dir()
|
|
983
|
+
)
|
|
984
|
+
|
|
985
|
+
for subagent_meta in list_subagents(
|
|
986
|
+
user_agents_dir=user_agents_dir,
|
|
987
|
+
project_agents_dir=project_agents_dir,
|
|
988
|
+
):
|
|
989
|
+
subagent: SubAgent = {
|
|
990
|
+
"name": subagent_meta["name"],
|
|
991
|
+
"description": subagent_meta["description"],
|
|
992
|
+
"system_prompt": subagent_meta["system_prompt"],
|
|
993
|
+
}
|
|
994
|
+
if subagent_meta["model"]:
|
|
995
|
+
subagent["model"] = subagent_meta["model"]
|
|
996
|
+
if restrictive_shell_allow_list is not None:
|
|
997
|
+
subagent["middleware"] = [
|
|
998
|
+
ShellAllowListMiddleware(restrictive_shell_allow_list)
|
|
999
|
+
]
|
|
1000
|
+
custom_subagents.append(subagent)
|
|
1001
|
+
|
|
1002
|
+
if restrictive_shell_allow_list is not None:
|
|
1003
|
+
from deepagents.middleware.subagents import (
|
|
1004
|
+
GENERAL_PURPOSE_SUBAGENT,
|
|
1005
|
+
SubAgent as RuntimeSubAgent,
|
|
1006
|
+
)
|
|
1007
|
+
|
|
1008
|
+
if not any(
|
|
1009
|
+
subagent["name"] == GENERAL_PURPOSE_SUBAGENT["name"]
|
|
1010
|
+
for subagent in custom_subagents
|
|
1011
|
+
):
|
|
1012
|
+
general_purpose_subagent: RuntimeSubAgent = {
|
|
1013
|
+
"name": GENERAL_PURPOSE_SUBAGENT["name"],
|
|
1014
|
+
"description": GENERAL_PURPOSE_SUBAGENT["description"],
|
|
1015
|
+
"system_prompt": GENERAL_PURPOSE_SUBAGENT["system_prompt"],
|
|
1016
|
+
"middleware": [ShellAllowListMiddleware(restrictive_shell_allow_list)],
|
|
1017
|
+
}
|
|
1018
|
+
custom_subagents.append(general_purpose_subagent)
|
|
1019
|
+
|
|
1020
|
+
# Build middleware stack based on enabled features
|
|
1021
|
+
agent_middleware = []
|
|
1022
|
+
agent_middleware.append(ConfigurableModelMiddleware())
|
|
1023
|
+
|
|
1024
|
+
# Token state: adds _context_tokens to graph state (checkpointed, not
|
|
1025
|
+
# passed to model). Must be registered before any middleware that might
|
|
1026
|
+
# read the channel.
|
|
1027
|
+
from docagent_cli.token_state import TokenStateMiddleware
|
|
1028
|
+
|
|
1029
|
+
agent_middleware.append(TokenStateMiddleware())
|
|
1030
|
+
|
|
1031
|
+
# Add ask_user middleware (must be early so its tool is available)
|
|
1032
|
+
if enable_ask_user:
|
|
1033
|
+
from docagent_cli.ask_user import AskUserMiddleware
|
|
1034
|
+
|
|
1035
|
+
agent_middleware.append(AskUserMiddleware())
|
|
1036
|
+
|
|
1037
|
+
# Add memory middleware
|
|
1038
|
+
if enable_memory:
|
|
1039
|
+
memory_sources = [str(settings.get_user_agent_md_path(assistant_id))]
|
|
1040
|
+
project_agent_md_paths = (
|
|
1041
|
+
project_context.project_agent_md_paths()
|
|
1042
|
+
if project_context is not None
|
|
1043
|
+
else settings.get_project_agent_md_path()
|
|
1044
|
+
)
|
|
1045
|
+
memory_sources.extend(str(p) for p in project_agent_md_paths)
|
|
1046
|
+
|
|
1047
|
+
agent_middleware.append(
|
|
1048
|
+
MemoryMiddleware(
|
|
1049
|
+
backend=FilesystemBackend(),
|
|
1050
|
+
sources=memory_sources,
|
|
1051
|
+
)
|
|
1052
|
+
)
|
|
1053
|
+
|
|
1054
|
+
# Add skills middleware
|
|
1055
|
+
if enable_skills:
|
|
1056
|
+
# Lowest to highest precedence:
|
|
1057
|
+
# built-in -> user .docagent -> user .agents
|
|
1058
|
+
# -> project .docagent -> project .agents
|
|
1059
|
+
# -> user .claude (experimental) -> project .claude (experimental)
|
|
1060
|
+
sources = [str(settings.get_built_in_skills_dir())]
|
|
1061
|
+
sources.extend([str(skills_dir), str(user_agent_skills_dir)])
|
|
1062
|
+
if project_skills_dir:
|
|
1063
|
+
sources.append(str(project_skills_dir))
|
|
1064
|
+
if project_agent_skills_dir:
|
|
1065
|
+
sources.append(str(project_agent_skills_dir))
|
|
1066
|
+
|
|
1067
|
+
# Experimental: Claude Code skill directories
|
|
1068
|
+
user_claude_skills_dir = settings.get_user_claude_skills_dir()
|
|
1069
|
+
if user_claude_skills_dir.exists():
|
|
1070
|
+
sources.append(str(user_claude_skills_dir))
|
|
1071
|
+
project_claude_skills_dir = settings.get_project_claude_skills_dir()
|
|
1072
|
+
if project_claude_skills_dir:
|
|
1073
|
+
sources.append(str(project_claude_skills_dir))
|
|
1074
|
+
|
|
1075
|
+
agent_middleware.append(
|
|
1076
|
+
SkillsMiddleware(
|
|
1077
|
+
backend=FilesystemBackend(),
|
|
1078
|
+
sources=sources,
|
|
1079
|
+
)
|
|
1080
|
+
)
|
|
1081
|
+
|
|
1082
|
+
# CONDITIONAL SETUP: Local vs Remote Sandbox
|
|
1083
|
+
if sandbox is None:
|
|
1084
|
+
# ========== LOCAL MODE ==========
|
|
1085
|
+
root_dir = effective_cwd if effective_cwd is not None else Path.cwd()
|
|
1086
|
+
if enable_shell:
|
|
1087
|
+
# Create environment for shell commands
|
|
1088
|
+
# Restore user's original LANGSMITH_PROJECT so their code traces separately
|
|
1089
|
+
shell_env = os.environ.copy()
|
|
1090
|
+
if settings.user_langchain_project:
|
|
1091
|
+
shell_env["LANGSMITH_PROJECT"] = settings.user_langchain_project
|
|
1092
|
+
|
|
1093
|
+
# Use LocalShellBackend for filesystem + shell execution.
|
|
1094
|
+
# The SDK's FilesystemMiddleware exposes per-command timeout
|
|
1095
|
+
# on the execute tool natively.
|
|
1096
|
+
backend = LocalShellBackend(
|
|
1097
|
+
root_dir=root_dir,
|
|
1098
|
+
inherit_env=True,
|
|
1099
|
+
env=shell_env,
|
|
1100
|
+
)
|
|
1101
|
+
else:
|
|
1102
|
+
# No shell access - use plain FilesystemBackend
|
|
1103
|
+
backend = FilesystemBackend(root_dir=root_dir)
|
|
1104
|
+
else:
|
|
1105
|
+
# ========== REMOTE SANDBOX MODE ==========
|
|
1106
|
+
backend = sandbox # Remote sandbox (ModalSandbox, etc.)
|
|
1107
|
+
# Note: Shell middleware not used in sandbox mode
|
|
1108
|
+
# File operations and execute tool are provided by the sandbox backend
|
|
1109
|
+
|
|
1110
|
+
# Local context middleware (git info, directory tree, etc.).
|
|
1111
|
+
if isinstance(backend, (_ExecutableBackend, _AsyncExecutableBackend)):
|
|
1112
|
+
agent_middleware.append(
|
|
1113
|
+
LocalContextMiddleware(backend=backend, mcp_server_info=mcp_server_info)
|
|
1114
|
+
)
|
|
1115
|
+
|
|
1116
|
+
# Add shell allow-list middleware when interrupt_shell_only is active.
|
|
1117
|
+
shell_middleware_added = False
|
|
1118
|
+
if restrictive_shell_allow_list is not None:
|
|
1119
|
+
agent_middleware.append(ShellAllowListMiddleware(restrictive_shell_allow_list))
|
|
1120
|
+
shell_middleware_added = True
|
|
1121
|
+
|
|
1122
|
+
# Get or use custom system prompt
|
|
1123
|
+
if system_prompt is None:
|
|
1124
|
+
system_prompt = get_system_prompt(
|
|
1125
|
+
assistant_id=assistant_id,
|
|
1126
|
+
sandbox_type=sandbox_type,
|
|
1127
|
+
interactive=interactive,
|
|
1128
|
+
cwd=effective_cwd,
|
|
1129
|
+
)
|
|
1130
|
+
|
|
1131
|
+
# Configure interrupt_on based on auto_approve / shell_middleware_added
|
|
1132
|
+
interrupt_on: dict[str, bool | InterruptOnConfig] | None = None
|
|
1133
|
+
if auto_approve or shell_middleware_added: # noqa: SIM108 # if-else clearer than ternary for dual-path config
|
|
1134
|
+
# No HITL interrupts — tools run automatically.
|
|
1135
|
+
# When shell_middleware_added is True, shell validation is handled by
|
|
1136
|
+
# ShellAllowListMiddleware (added above) which rejects disallowed
|
|
1137
|
+
# commands inline as error ToolMessages, keeping the entire run in
|
|
1138
|
+
# a single LangSmith trace.
|
|
1139
|
+
interrupt_on = {}
|
|
1140
|
+
else:
|
|
1141
|
+
# Full HITL for destructive operations
|
|
1142
|
+
interrupt_on = _add_interrupt_on() # type: ignore[assignment] # InterruptOnConfig is compatible at runtime
|
|
1143
|
+
|
|
1144
|
+
# Set up composite backend with routing
|
|
1145
|
+
# For local FilesystemBackend, route large tool results to /tmp to avoid polluting
|
|
1146
|
+
# the working directory. For sandbox backends, no special routing is needed.
|
|
1147
|
+
if sandbox is None:
|
|
1148
|
+
# Local mode: Route large results to a unique temp directory
|
|
1149
|
+
large_results_backend = FilesystemBackend(
|
|
1150
|
+
root_dir=tempfile.mkdtemp(prefix="docagent_large_results_"),
|
|
1151
|
+
virtual_mode=True,
|
|
1152
|
+
)
|
|
1153
|
+
conversation_history_backend = FilesystemBackend(
|
|
1154
|
+
root_dir=tempfile.mkdtemp(prefix="docagent_conversation_history_"),
|
|
1155
|
+
virtual_mode=True,
|
|
1156
|
+
)
|
|
1157
|
+
composite_backend = CompositeBackend(
|
|
1158
|
+
default=backend,
|
|
1159
|
+
routes={
|
|
1160
|
+
"/large_tool_results/": large_results_backend,
|
|
1161
|
+
"/conversation_history/": conversation_history_backend,
|
|
1162
|
+
},
|
|
1163
|
+
)
|
|
1164
|
+
else:
|
|
1165
|
+
# Sandbox mode: No special routing needed
|
|
1166
|
+
composite_backend = CompositeBackend(
|
|
1167
|
+
default=backend,
|
|
1168
|
+
routes={},
|
|
1169
|
+
)
|
|
1170
|
+
|
|
1171
|
+
from deepagents.middleware.summarization import create_summarization_tool_middleware
|
|
1172
|
+
|
|
1173
|
+
agent_middleware.append(
|
|
1174
|
+
create_summarization_tool_middleware(model, composite_backend)
|
|
1175
|
+
)
|
|
1176
|
+
|
|
1177
|
+
# Create the agent
|
|
1178
|
+
all_subagents: list[SubAgent | CompiledSubAgent | AsyncSubAgent] = [
|
|
1179
|
+
*custom_subagents,
|
|
1180
|
+
*(async_subagents or []),
|
|
1181
|
+
]
|
|
1182
|
+
agent = create_deep_agent(
|
|
1183
|
+
model=model,
|
|
1184
|
+
system_prompt=system_prompt,
|
|
1185
|
+
tools=tools,
|
|
1186
|
+
backend=composite_backend,
|
|
1187
|
+
middleware=agent_middleware,
|
|
1188
|
+
interrupt_on=interrupt_on,
|
|
1189
|
+
checkpointer=checkpointer,
|
|
1190
|
+
subagents=all_subagents or None,
|
|
1191
|
+
extra_subagent_middleware=[ConfigurableModelMiddleware()],
|
|
1192
|
+
).with_config(config)
|
|
1193
|
+
return agent, composite_backend
|