iac-code 0.4.0__tar.gz → 0.4.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (309) hide show
  1. {iac_code-0.4.0 → iac_code-0.4.1}/PKG-INFO +1 -1
  2. iac_code-0.4.1/src/iac_code/__init__.py +2 -0
  3. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/executor.py +18 -2
  4. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/acp/session.py +15 -1
  5. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/acp/tools.py +39 -4
  6. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/agent/agent_loop.py +60 -8
  7. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/agent/agent_tool.py +79 -25
  8. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/agent/agent_types.py +11 -9
  9. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/cli/main.py +6 -0
  10. iac_code-0.4.1/src/iac_code/cli/update.py +38 -0
  11. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/auth.py +2 -0
  12. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/model.py +25 -10
  13. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/status.py +8 -2
  14. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/tasks.py +2 -1
  15. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/config.py +3 -2
  16. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/i18n/__init__.py +13 -1
  17. iac_code-0.4.1/src/iac_code/i18n/locales/de/LC_MESSAGES/messages.mo +0 -0
  18. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/i18n/locales/de/LC_MESSAGES/messages.po +873 -676
  19. iac_code-0.4.1/src/iac_code/i18n/locales/es/LC_MESSAGES/messages.mo +0 -0
  20. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/i18n/locales/es/LC_MESSAGES/messages.po +874 -676
  21. iac_code-0.4.1/src/iac_code/i18n/locales/fr/LC_MESSAGES/messages.mo +0 -0
  22. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/i18n/locales/fr/LC_MESSAGES/messages.po +877 -676
  23. iac_code-0.4.1/src/iac_code/i18n/locales/ja/LC_MESSAGES/messages.mo +0 -0
  24. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/i18n/locales/ja/LC_MESSAGES/messages.po +861 -676
  25. iac_code-0.4.1/src/iac_code/i18n/locales/pt/LC_MESSAGES/messages.mo +0 -0
  26. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/i18n/locales/pt/LC_MESSAGES/messages.po +874 -676
  27. iac_code-0.4.1/src/iac_code/i18n/locales/zh/LC_MESSAGES/messages.mo +0 -0
  28. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/i18n/locales/zh/LC_MESSAGES/messages.po +861 -676
  29. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/memory/memory_tools.py +2 -2
  30. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/manager.py +82 -27
  31. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/openai_provider.py +9 -0
  32. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/openrouter_provider.py +12 -9
  33. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/agent_factory.py +2 -0
  34. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/context_manager.py +51 -3
  35. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/permissions/loader.py +1 -0
  36. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/permissions/pipeline.py +32 -2
  37. iac_code-0.4.1/src/iac_code/services/permissions/trusted_roots.py +21 -0
  38. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/providers/aliyun.py +48 -25
  39. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/providers/aliyun_oauth.py +23 -7
  40. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/session_index.py +26 -1
  41. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/__init__.py +2 -1
  42. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/identity.py +27 -1
  43. iac_code-0.4.1/src/iac_code/services/token_counter.py +149 -0
  44. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/update_checker.py +8 -3
  45. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/renderer.py +119 -30
  46. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/skill_tool.py +3 -1
  47. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tasks/task_state.py +20 -5
  48. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tasks/task_tools.py +2 -1
  49. iac_code-0.4.1/src/iac_code/tools/bash/argv_safety.py +1112 -0
  50. iac_code-0.4.1/src/iac_code/tools/bash/path_validation.py +249 -0
  51. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/bash/permissions.py +33 -28
  52. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/bash/readonly_commands.py +9 -24
  53. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/bash/safety_checks.py +10 -53
  54. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/ros_stack.py +313 -139
  55. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/base_stack.py +111 -49
  56. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/types.py +10 -1
  57. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/edit_file.py +24 -0
  58. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/glob.py +56 -0
  59. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/grep.py +127 -4
  60. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/list_files.py +24 -5
  61. iac_code-0.4.1/src/iac_code/tools/path_safety.py +218 -0
  62. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/read_file.py +99 -19
  63. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/tool_executor.py +1 -9
  64. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/web_fetch.py +54 -6
  65. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/write_file.py +24 -0
  66. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/types/permissions.py +1 -0
  67. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/types/stream_events.py +9 -0
  68. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/core/input_history.py +5 -5
  69. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/core/prompt_input.py +10 -8
  70. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/dialogs/resume_picker.py +6 -6
  71. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/renderer.py +246 -46
  72. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/repl.py +91 -24
  73. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/suggestions/shell_history_provider.py +36 -2
  74. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/file_security.py +19 -0
  75. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code.egg-info/PKG-INFO +1 -1
  76. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code.egg-info/SOURCES.txt +4 -0
  77. iac_code-0.4.0/src/iac_code/__init__.py +0 -2
  78. iac_code-0.4.0/src/iac_code/i18n/locales/de/LC_MESSAGES/messages.mo +0 -0
  79. iac_code-0.4.0/src/iac_code/i18n/locales/es/LC_MESSAGES/messages.mo +0 -0
  80. iac_code-0.4.0/src/iac_code/i18n/locales/fr/LC_MESSAGES/messages.mo +0 -0
  81. iac_code-0.4.0/src/iac_code/i18n/locales/ja/LC_MESSAGES/messages.mo +0 -0
  82. iac_code-0.4.0/src/iac_code/i18n/locales/pt/LC_MESSAGES/messages.mo +0 -0
  83. iac_code-0.4.0/src/iac_code/i18n/locales/zh/LC_MESSAGES/messages.mo +0 -0
  84. iac_code-0.4.0/src/iac_code/services/token_counter.py +0 -81
  85. iac_code-0.4.0/src/iac_code/tools/bash/path_validation.py +0 -118
  86. {iac_code-0.4.0 → iac_code-0.4.1}/LICENSE +0 -0
  87. {iac_code-0.4.0 → iac_code-0.4.1}/MANIFEST.in +0 -0
  88. {iac_code-0.4.0 → iac_code-0.4.1}/README.md +0 -0
  89. {iac_code-0.4.0 → iac_code-0.4.1}/pyproject.toml +0 -0
  90. {iac_code-0.4.0 → iac_code-0.4.1}/setup.cfg +0 -0
  91. {iac_code-0.4.0 → iac_code-0.4.1}/setup.py +0 -0
  92. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/__init__.py +0 -0
  93. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/agent_card.py +0 -0
  94. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/app.py +0 -0
  95. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/artifacts.py +0 -0
  96. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/client.py +0 -0
  97. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/events.py +0 -0
  98. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/exposure.py +0 -0
  99. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/metrics.py +0 -0
  100. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/parts.py +0 -0
  101. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/persistence.py +0 -0
  102. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/push.py +0 -0
  103. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/push_queue.py +0 -0
  104. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/push_secrets.py +0 -0
  105. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/push_worker.py +0 -0
  106. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/router.py +0 -0
  107. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/signing.py +0 -0
  108. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/task_store.py +0 -0
  109. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/transport.py +0 -0
  110. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/transports/__init__.py +0 -0
  111. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/transports/base.py +0 -0
  112. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/transports/dispatcher.py +0 -0
  113. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/transports/grpc.py +0 -0
  114. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/transports/grpc_jsonrpc.py +0 -0
  115. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/transports/http.py +0 -0
  116. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/transports/redis_streams.py +0 -0
  117. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/transports/stdio.py +0 -0
  118. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/transports/unix.py +0 -0
  119. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/transports/websocket.py +0 -0
  120. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/a2a/types.py +0 -0
  121. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/acp/__init__.py +0 -0
  122. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/acp/convert.py +0 -0
  123. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/acp/http_sse.py +0 -0
  124. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/acp/mcp.py +0 -0
  125. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/acp/metrics.py +0 -0
  126. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/acp/server.py +0 -0
  127. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/acp/slash_registry.py +0 -0
  128. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/acp/state.py +0 -0
  129. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/acp/types.py +0 -0
  130. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/acp/version.py +0 -0
  131. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/agent/__init__.py +0 -0
  132. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/agent/message.py +0 -0
  133. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/agent/system_prompt.py +0 -0
  134. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/cli/__init__.py +0 -0
  135. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/cli/headless.py +0 -0
  136. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/cli/install_git_bash.py +0 -0
  137. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/cli/output_formats.py +0 -0
  138. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/__init__.py +0 -0
  139. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/clear.py +0 -0
  140. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/compact.py +0 -0
  141. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/debug.py +0 -0
  142. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/effort.py +0 -0
  143. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/exit.py +0 -0
  144. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/help.py +0 -0
  145. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/memory.py +0 -0
  146. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/registry.py +0 -0
  147. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/rename.py +0 -0
  148. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/resume.py +0 -0
  149. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/commands/skills.py +0 -0
  150. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/memory/__init__.py +0 -0
  151. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/memory/memory_manager.py +0 -0
  152. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/__init__.py +0 -0
  153. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/anthropic_provider.py +0 -0
  154. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/azure_openai_provider.py +0 -0
  155. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/base.py +0 -0
  156. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/dashscope_provider.py +0 -0
  157. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/deepseek_provider.py +0 -0
  158. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/gemini_provider.py +0 -0
  159. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/kimi_provider.py +0 -0
  160. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/lmstudio_provider.py +0 -0
  161. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/minimax_provider.py +0 -0
  162. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/modelscope_provider.py +0 -0
  163. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/ollama_provider.py +0 -0
  164. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/registry.py +0 -0
  165. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/retry.py +0 -0
  166. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/siliconflow_provider.py +0 -0
  167. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/stream_watchdog.py +0 -0
  168. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/thinking.py +0 -0
  169. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/volcengine_provider.py +0 -0
  170. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/providers/zhipu_provider.py +0 -0
  171. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/__init__.py +0 -0
  172. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/capabilities/__init__.py +0 -0
  173. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/capabilities/auto_detect.py +0 -0
  174. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/capabilities/multimodal.py +0 -0
  175. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/cloud_credentials.py +0 -0
  176. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/permissions/__init__.py +0 -0
  177. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/permissions/storage.py +0 -0
  178. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/providers/__init__.py +0 -0
  179. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/qwenpaw_source.py +0 -0
  180. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/session_metadata.py +0 -0
  181. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/session_resolver.py +0 -0
  182. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/session_storage.py +0 -0
  183. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/session_usage.py +0 -0
  184. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/attributes.py +0 -0
  185. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/client.py +0 -0
  186. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/config.py +0 -0
  187. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/constants.py +0 -0
  188. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/content_serializer.py +0 -0
  189. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/events.py +0 -0
  190. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/fallback.py +0 -0
  191. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/metrics.py +0 -0
  192. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/names.py +0 -0
  193. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/sanitize.py +0 -0
  194. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/sink.py +0 -0
  195. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/tracing.py +0 -0
  196. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/telemetry/types.py +0 -0
  197. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/services/token_budget.py +0 -0
  198. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/__init__.py +0 -0
  199. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/auto_trigger.py +0 -0
  200. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/__init__.py +0 -0
  201. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/SKILL.md +0 -0
  202. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/__init__.py +0 -0
  203. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/auto_trigger.py +0 -0
  204. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/ecs.md +0 -0
  205. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/oss.md +0 -0
  206. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/rds.md +0 -0
  207. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/redis.md +0 -0
  208. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/slb.md +0 -0
  209. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/vpc.md +0 -0
  210. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/references/ros-template.md +0 -0
  211. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/references/template-parameter-recommendation.md +0 -0
  212. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/references/template-parameters.md +0 -0
  213. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/references/terraform-template.md +0 -0
  214. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/scripts/tf2ros.py +0 -0
  215. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/bundled/simplify.py +0 -0
  216. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/discovery.py +0 -0
  217. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/frontmatter.py +0 -0
  218. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/listing.py +0 -0
  219. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/loader.py +0 -0
  220. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/management.py +0 -0
  221. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/processor.py +0 -0
  222. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/settings.py +0 -0
  223. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/skills/skill_definition.py +0 -0
  224. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/state/__init__.py +0 -0
  225. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/state/app_state.py +0 -0
  226. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tasks/__init__.py +0 -0
  227. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tasks/notification_queue.py +0 -0
  228. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/__init__.py +0 -0
  229. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/base.py +0 -0
  230. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/bash/__init__.py +0 -0
  231. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/bash/bash_tool.py +0 -0
  232. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/bash/command_parser.py +0 -0
  233. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/bash/mode_validation.py +0 -0
  234. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/bash/rule_matching.py +0 -0
  235. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/__init__.py +0 -0
  236. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/__init__.py +0 -0
  237. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/aliyun_api.py +0 -0
  238. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/aliyun_doc_search.py +0 -0
  239. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/api_hooks.py +0 -0
  240. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/endpoints.yml +0 -0
  241. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/hooks/__init__.py +0 -0
  242. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/hooks/ros_parameters.py +0 -0
  243. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/hooks/ros_validate.py +0 -0
  244. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/ros_client.py +0 -0
  245. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/ros_stack_instances.py +0 -0
  246. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/ros_yaml.py +0 -0
  247. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/user_agent.py +0 -0
  248. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/base_api.py +0 -0
  249. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/cloud/registry.py +0 -0
  250. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/tools/result_storage.py +0 -0
  251. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/types/__init__.py +0 -0
  252. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/types/skill_source.py +0 -0
  253. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/__init__.py +0 -0
  254. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/banner.py +0 -0
  255. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/components/__init__.py +0 -0
  256. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/components/dialog.py +0 -0
  257. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/components/divider.py +0 -0
  258. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/components/fuzzy_picker.py +0 -0
  259. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/components/progress_bar.py +0 -0
  260. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/components/search_box.py +0 -0
  261. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/components/select.py +0 -0
  262. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/components/status_icon.py +0 -0
  263. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/components/tabs.py +0 -0
  264. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/core/__init__.py +0 -0
  265. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/core/in_place_render.py +0 -0
  266. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/core/key_event.py +0 -0
  267. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/core/raw_input.py +0 -0
  268. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/core/raw_input_win.py +0 -0
  269. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/core/screen.py +0 -0
  270. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/dialogs/__init__.py +0 -0
  271. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/dialogs/global_search.py +0 -0
  272. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/dialogs/history_search.py +0 -0
  273. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/dialogs/model_picker.py +0 -0
  274. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/dialogs/quick_open.py +0 -0
  275. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/dialogs/skills_picker.py +0 -0
  276. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/keybindings/__init__.py +0 -0
  277. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/keybindings/manager.py +0 -0
  278. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/spinner.py +0 -0
  279. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/suggestions/__init__.py +0 -0
  280. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/suggestions/aggregator.py +0 -0
  281. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/suggestions/command_provider.py +0 -0
  282. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/suggestions/directory_provider.py +0 -0
  283. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/suggestions/file_provider.py +0 -0
  284. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/suggestions/skill_provider.py +0 -0
  285. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/suggestions/token_extractor.py +0 -0
  286. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/suggestions/types.py +0 -0
  287. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/ui/transcript_view.py +0 -0
  288. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/__init__.py +0 -0
  289. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/background_housekeeping.py +0 -0
  290. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/cleanup.py +0 -0
  291. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/console.py +0 -0
  292. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/image/__init__.py +0 -0
  293. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/image/clipboard.py +0 -0
  294. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/image/format_detect.py +0 -0
  295. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/image/pasted_content.py +0 -0
  296. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/image/processor.py +0 -0
  297. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/image/resizer.py +0 -0
  298. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/image/store.py +0 -0
  299. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/json_utils.py +0 -0
  300. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/log.py +0 -0
  301. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/platform.py +0 -0
  302. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/project_paths.py +0 -0
  303. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/signals.py +0 -0
  304. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/tool_input_parser.py +0 -0
  305. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code/utils/windows_paths.py +0 -0
  306. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code.egg-info/dependency_links.txt +0 -0
  307. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code.egg-info/entry_points.txt +0 -0
  308. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code.egg-info/requires.txt +0 -0
  309. {iac_code-0.4.0 → iac_code-0.4.1}/src/iac_code.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: iac_code
3
- Version: 0.4.0
3
+ Version: 0.4.1
4
4
  Summary: Your AI-powered Infrastructure as Code assistant
5
5
  Classifier: Programming Language :: Python :: 3
6
6
  Classifier: Programming Language :: Python :: 3.10
@@ -0,0 +1,2 @@
1
+ __version__ = "0.4.1"
2
+ __release_date__ = "2026-06-05"
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
+ import contextlib
4
5
  import logging
5
6
  import os
6
7
  import uuid
@@ -27,7 +28,7 @@ from iac_code.a2a.types import (
27
28
  )
28
29
  from iac_code.services.agent_factory import AgentFactoryOptions, create_agent_runtime
29
30
  from iac_code.services.session_storage import SessionStorage
30
- from iac_code.services.telemetry import use_session_id
31
+ from iac_code.services.telemetry import use_session_id, use_user_id
31
32
 
32
33
  logger = logging.getLogger(__name__)
33
34
  _CONTEXT_LOCK_ACQUIRE_TIMEOUT_SECONDS = 1
@@ -87,6 +88,7 @@ class IacCodeA2AExecutor(AgentExecutor):
87
88
  getattr(context, "message", None), "metadata", None
88
89
  )
89
90
  cwd = self._resolve_cwd(metadata)
91
+ user_id = self._resolve_user_id(metadata)
90
92
  prompt = self._prompt_from_context(context, cwd=cwd)
91
93
  except Exception as exc:
92
94
  if _is_retryable_executor_error(exc):
@@ -222,7 +224,8 @@ class IacCodeA2AExecutor(AgentExecutor):
222
224
  context_id=context_id,
223
225
  state=TaskState.TASK_STATE_WORKING,
224
226
  )
225
- with use_session_id(ctx.session_id):
227
+ user_id_ctx = use_user_id(user_id) if user_id else contextlib.nullcontext()
228
+ with use_session_id(ctx.session_id), user_id_ctx:
226
229
  async for event in runtime.agent_loop.run_streaming(prompt):
227
230
  text_chunk = await publish_stream_event(
228
231
  event_queue,
@@ -348,6 +351,19 @@ class IacCodeA2AExecutor(AgentExecutor):
348
351
  resolved_cwd.mkdir(parents=True, exist_ok=True)
349
352
  return str(resolved_cwd)
350
353
 
354
+ def _resolve_user_id(self, metadata: Any | None) -> str | None:
355
+ if metadata is not None and hasattr(metadata, "DESCRIPTOR"):
356
+ metadata = MessageToDict(metadata, preserving_proto_field_name=False)
357
+ if not isinstance(metadata, Mapping):
358
+ return None
359
+ raw_iac_meta = metadata.get("iac_code")
360
+ if not isinstance(raw_iac_meta, Mapping):
361
+ return None
362
+ raw_user_id = raw_iac_meta.get("user_id")
363
+ if isinstance(raw_user_id, str) and raw_user_id.strip():
364
+ return raw_user_id.strip()
365
+ return None
366
+
351
367
  def _prompt_from_context(self, context: RequestContext, *, cwd: str) -> str:
352
368
  message = getattr(context, "message", None)
353
369
  if not isinstance(message, Message):
@@ -157,6 +157,20 @@ _PREFIX_ALLOW_RULE = "allow_rule:"
157
157
  _PREFIX_DENY_RULE = "deny_rule:"
158
158
 
159
159
 
160
+ def _tool_supports_blanket_allow(agent_loop, tool_name: str) -> bool:
161
+ """Return False only when the registered tool explicitly disables blanket allow."""
162
+ registry = getattr(agent_loop, "tool_registry", None)
163
+ get_tool = getattr(registry, "get", None)
164
+ if get_tool is None:
165
+ return True
166
+
167
+ tool = get_tool(tool_name)
168
+ if tool is None:
169
+ return True
170
+
171
+ return bool(getattr(tool, "supports_blanket_allow", True))
172
+
173
+
160
174
  class ACPSession:
161
175
  def __init__(
162
176
  self,
@@ -457,7 +471,7 @@ class ACPSession:
457
471
  kind="allow_always",
458
472
  )
459
473
  )
460
- else:
474
+ elif _tool_supports_blanket_allow(self.agent_loop, tool_name):
461
475
  options.append(
462
476
  acp.schema.PermissionOption(
463
477
  option_id=_OPTION_ALLOW_ALWAYS,
@@ -5,6 +5,7 @@ from contextlib import suppress
5
5
 
6
6
  import acp
7
7
 
8
+ from iac_code.i18n import _
8
9
  from iac_code.tools.base import Tool, ToolContext, ToolResult
9
10
 
10
11
  TERMINAL_TIMEOUT = 300 # 5 minutes default timeout
@@ -32,9 +33,37 @@ class ACPTerminalBashTool(Tool):
32
33
  def timeout(self) -> float | None:
33
34
  return self._original.timeout
34
35
 
36
+ @property
37
+ def supports_blanket_allow(self) -> bool:
38
+ return self._original.supports_blanket_allow
39
+
40
+ def user_facing_name(self, input: dict | None = None) -> str:
41
+ return self._original.user_facing_name(input)
42
+
43
+ def get_activity_description(self, input: dict | None = None) -> str | None:
44
+ return self._original.get_activity_description(input)
45
+
46
+ def get_tool_use_summary(self, input: dict | None = None) -> str | None:
47
+ return self._original.get_tool_use_summary(input)
48
+
49
+ def render_tool_use_message(self, input: dict, *, verbose: bool = False) -> str | None:
50
+ return self._original.render_tool_use_message(input, verbose=verbose)
51
+
52
+ def render_tool_result_message(self, output: str, *, is_error: bool = False, verbose: bool = False) -> str | None:
53
+ return self._original.render_tool_result_message(output, is_error=is_error, verbose=verbose)
54
+
55
+ def render_tool_use_error_message(self, error: str) -> str | None:
56
+ return self._original.render_tool_use_error_message(error)
57
+
58
+ def streaming_preview_fields(self) -> list[str]:
59
+ return self._original.streaming_preview_fields()
60
+
35
61
  def is_read_only(self, input: dict | None = None) -> bool:
36
62
  return self._original.is_read_only(input)
37
63
 
64
+ def is_concurrency_safe(self, tool_input: dict) -> bool:
65
+ return self._original.is_concurrency_safe(tool_input)
66
+
38
67
  def is_destructive(self, input: dict | None = None) -> bool:
39
68
  return self._original.is_destructive(input)
40
69
 
@@ -44,7 +73,7 @@ class ACPTerminalBashTool(Tool):
44
73
  async def execute(self, *, tool_input: dict, context: ToolContext) -> ToolResult:
45
74
  command = tool_input.get("command")
46
75
  if not command:
47
- return ToolResult.error("Bash command is required.")
76
+ return ToolResult.error(_("Bash command is required."))
48
77
 
49
78
  timeout = tool_input.get("timeout", TERMINAL_TIMEOUT)
50
79
  terminal_id: str | None = None
@@ -74,7 +103,7 @@ class ACPTerminalBashTool(Tool):
74
103
  except asyncio.TimeoutError:
75
104
  with suppress(Exception):
76
105
  await self._conn.kill_terminal(session_id=self._session_id, terminal_id=terminal_id)
77
- return ToolResult.error(f"Command timed out after {timeout} seconds")
106
+ return ToolResult.error(_("Command timed out after {timeout} seconds").format(timeout=timeout))
78
107
 
79
108
  if output.exit_status:
80
109
  exit_status = output.exit_status
@@ -84,9 +113,15 @@ class ACPTerminalBashTool(Tool):
84
113
  # at the session layer in a future phase.
85
114
 
86
115
  if exit_status.signal:
87
- return ToolResult.error(f"Command terminated by signal: {exit_status.signal}\n{output.output}")
116
+ return ToolResult.error(
117
+ _("Command terminated by signal: {signal}").format(signal=exit_status.signal) + "\n" + output.output
118
+ )
88
119
  if exit_status.exit_code not in (None, 0):
89
- return ToolResult.error(f"Command failed with exit code {exit_status.exit_code}\n{output.output}")
120
+ return ToolResult.error(
121
+ _("Command failed with exit code {exit_code}").format(exit_code=exit_status.exit_code)
122
+ + "\n"
123
+ + output.output
124
+ )
90
125
  return ToolResult.success(output.output)
91
126
  except asyncio.CancelledError:
92
127
  if terminal_id is not None:
@@ -6,7 +6,7 @@ import asyncio
6
6
  import os
7
7
  import time
8
8
  import uuid
9
- from collections.abc import AsyncGenerator
9
+ from collections.abc import AsyncGenerator, Callable
10
10
  from dataclasses import dataclass
11
11
  from typing import Any, Literal
12
12
 
@@ -23,6 +23,7 @@ from iac_code.types.stream_events import (
23
23
  CompactionEvent,
24
24
  MessageEndEvent,
25
25
  PermissionRequestEvent,
26
+ QueuedInputSubmittedEvent,
26
27
  StackInstancesProgressEvent,
27
28
  StackProgressEvent,
28
29
  StreamEvent,
@@ -94,6 +95,7 @@ class AgentLoop:
94
95
  model_name = provider_manager.get_model_name()
95
96
 
96
97
  self.context_manager = ContextManager(system_prompt=system_prompt, model=model_name)
98
+ self._sync_tool_definitions()
97
99
  if resume_messages:
98
100
  self.context_manager.load_messages(resume_messages)
99
101
  self._tool_executor = ToolExecutor(registry=tool_registry)
@@ -116,6 +118,7 @@ class AgentLoop:
116
118
  if system_prompt is not None:
117
119
  self.system_prompt = system_prompt
118
120
  self.context_manager.set_system_prompt(system_prompt)
121
+ self._sync_tool_definitions()
119
122
 
120
123
  def set_auto_trigger_skills(self, skill_commands: list[Any] | None) -> None:
121
124
  """Refresh skills considered for automatic trigger injection."""
@@ -136,6 +139,12 @@ class AgentLoop:
136
139
  )
137
140
  return tools
138
141
 
142
+ def _sync_tool_definitions(self):
143
+ """Refresh context token accounting from the current tool registry."""
144
+ tool_definitions = self._get_tool_definitions()
145
+ self.context_manager.set_tool_definitions(tool_definitions)
146
+ return tool_definitions
147
+
139
148
  def _get_provider_messages(self):
140
149
  """Convert context manager messages to provider Message format."""
141
150
  from iac_code.providers.base import ContentBlock
@@ -178,7 +187,11 @@ class AgentLoop:
178
187
  final_text += event.text
179
188
  return final_text
180
189
 
181
- async def run_streaming(self, user_input: str | list[ContentBlock]) -> AsyncGenerator[StreamEvent, None]:
190
+ async def run_streaming(
191
+ self,
192
+ user_input: str | list[ContentBlock],
193
+ queued_input_provider: Callable[[], list[str]] | None = None,
194
+ ) -> AsyncGenerator[StreamEvent, None]:
182
195
  """Streaming execution yielding fine-grained StreamEvents.
183
196
 
184
197
  Flow:
@@ -248,7 +261,10 @@ class AgentLoop:
248
261
  git_branch=self._current_git_branch,
249
262
  )
250
263
  try:
251
- async for event in self._run_streaming_inner(user_input):
264
+ async for event in self._run_streaming_inner(
265
+ user_input,
266
+ queued_input_provider=queued_input_provider,
267
+ ):
252
268
  if isinstance(event, TextDeltaEvent) and not first_token_received:
253
269
  first_token_received = True
254
270
  ttft_ns = int((time.monotonic() - interaction_started) * 1_000_000_000)
@@ -272,14 +288,18 @@ class AgentLoop:
272
288
  serialize_output_messages("".join(final_text_chunks), final_stop_reason),
273
289
  )
274
290
 
275
- async def _run_streaming_inner(self, user_input: str | list[ContentBlock]) -> AsyncGenerator[StreamEvent, None]:
291
+ async def _run_streaming_inner(
292
+ self,
293
+ user_input: str | list[ContentBlock],
294
+ queued_input_provider: Callable[[], list[str]] | None = None,
295
+ ) -> AsyncGenerator[StreamEvent, None]:
276
296
  """Inner streaming loop (called from run_streaming inside the ENTRY span)."""
277
297
  from iac_code.services.telemetry import start_span
278
298
  from iac_code.services.telemetry.names import GenAiAttr, GenAiOperationName, GenAiSpanKind, Spans
279
299
 
280
- tool_definitions = self._get_tool_definitions()
281
-
282
300
  for _turn in range(self._max_turns):
301
+ tool_definitions = self._sync_tool_definitions()
302
+
283
303
  # Auto-compact if needed
284
304
  if self.context_manager.needs_compaction():
285
305
  compact_event = await self._auto_compact()
@@ -303,7 +323,7 @@ class AgentLoop:
303
323
  async for event in self._provider_manager.stream(
304
324
  messages=self._get_provider_messages(),
305
325
  system=self.system_prompt,
306
- tools=tool_definitions if self.tool_registry.list_tools() else None,
326
+ tools=tool_definitions if tool_definitions else None,
307
327
  ):
308
328
  yield event # Forward all provider events to UI
309
329
 
@@ -329,6 +349,7 @@ class AgentLoop:
329
349
 
330
350
  if not message_ended:
331
351
  step_span.set_attribute(GenAiAttr.REACT_FINISH_REASON, "error")
352
+ yield MessageEndEvent(stop_reason="stream_error", usage=Usage())
332
353
  break
333
354
 
334
355
  # Build assistant message for context
@@ -451,8 +472,13 @@ class AgentLoop:
451
472
 
452
473
  denied_content: list[ContentBlock] = list(denied_blocks)
453
474
  self._session_storage.append(
454
- self._cwd, self._session_id, Message(role="user", content=denied_content)
475
+ self._cwd,
476
+ self._session_id,
477
+ Message(role="user", content=denied_content),
478
+ git_branch=self._current_git_branch,
455
479
  )
480
+ async for event in self._submit_queued_inputs_after_tool_call(queued_input_provider):
481
+ yield event
456
482
  continue
457
483
 
458
484
  requests = allowed_requests
@@ -549,9 +575,35 @@ class AgentLoop:
549
575
  self.context_manager.add_raw_message(msg)
550
576
  if result.context_modifier is not None:
551
577
  self._apply_context_modifier(result.context_modifier)
578
+
579
+ async for event in self._submit_queued_inputs_after_tool_call(queued_input_provider):
580
+ yield event
552
581
  else:
553
582
  yield MessageEndEvent(stop_reason="max_turns", usage=Usage())
554
583
 
584
+ async def _submit_queued_inputs_after_tool_call(
585
+ self,
586
+ queued_input_provider: Callable[[], list[str]] | None,
587
+ ) -> AsyncGenerator[QueuedInputSubmittedEvent, None]:
588
+ if queued_input_provider is None:
589
+ return
590
+
591
+ queued_inputs = queued_input_provider()
592
+ for raw_input in queued_inputs:
593
+ text = raw_input.strip()
594
+ if not text:
595
+ continue
596
+ await self._apply_auto_triggers(text)
597
+ message = self.context_manager.add_user_message(text)
598
+ if self._session_storage:
599
+ self._session_storage.append(
600
+ self._cwd,
601
+ self._session_id,
602
+ message,
603
+ git_branch=self._current_git_branch,
604
+ )
605
+ yield QueuedInputSubmittedEvent(text=text)
606
+
555
607
  async def _apply_auto_triggers(self, user_input: str | list[ContentBlock]) -> None:
556
608
  if not self._auto_trigger_skills:
557
609
  return
@@ -3,11 +3,12 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import asyncio
6
+ from contextlib import suppress
6
7
  from dataclasses import dataclass
7
8
  from typing import Any
8
9
 
9
10
  from iac_code.agent.agent_types import filter_tools, get_agent_definition, get_builtin_agents
10
- from iac_code.i18n import _
11
+ from iac_code.i18n import _, ngettext
11
12
  from iac_code.tools.base import Tool, ToolContext, ToolResult
12
13
 
13
14
 
@@ -21,6 +22,14 @@ class AgentProgress:
21
22
  summary: str = ""
22
23
 
23
24
 
25
+ def _format_base_exception(error: BaseException) -> str:
26
+ detail = str(error)
27
+ error_type = type(error).__name__
28
+ if not detail:
29
+ return error_type
30
+ return "{error_type}: {detail}".format(error_type=error_type, detail=detail)
31
+
32
+
24
33
  async def run_sub_agent(
25
34
  *,
26
35
  prompt: str,
@@ -45,7 +54,7 @@ async def run_sub_agent(
45
54
 
46
55
  defn = get_agent_definition(agent_type)
47
56
  if defn is None:
48
- raise ValueError(f"Unknown agent type: {agent_type}")
57
+ raise ValueError(_("Unknown agent type: {agent_type}").format(agent_type=agent_type))
49
58
 
50
59
  sub_registry = filter_tools(parent_tool_registry, defn) if parent_tool_registry else parent_tool_registry
51
60
  system_prompt = parent_system_prompt or build_system_prompt(cwd=cwd)
@@ -58,7 +67,7 @@ async def run_sub_agent(
58
67
  permission_context=permission_context,
59
68
  )
60
69
 
61
- progress = AgentProgress(summary=f"Running {agent_type} agent")
70
+ progress = AgentProgress(summary=_("Running {agent_type} agent").format(agent_type=agent_type))
62
71
  text_chunks: list[str] = []
63
72
  # Track tool inputs: tool_use_id -> (name, input)
64
73
  pending_tool_inputs: dict[str, tuple[str, dict]] = {}
@@ -128,7 +137,6 @@ class AgentTool(Tool):
128
137
  self._tool_registry = tool_registry
129
138
  self._system_prompt = system_prompt
130
139
  self._permission_context = permission_context
131
- self._event_queue: asyncio.Queue | None = None # Set by ToolExecutor via ToolCallRequest
132
140
 
133
141
  @property
134
142
  def name(self) -> str:
@@ -137,8 +145,13 @@ class AgentTool(Tool):
137
145
  @property
138
146
  def description(self) -> str:
139
147
  agents = get_builtin_agents()
140
- agent_list = "\n".join(f" - {a.agent_type}: {a.when_to_use}" for a in agents)
141
- return f"Launch a sub-agent to handle complex tasks.\n\nAvailable agent types:\n{agent_list}"
148
+ agent_list = "\n".join(
149
+ " - {agent_type}: {when_to_use}".format(agent_type=agent.agent_type, when_to_use=agent.when_to_use)
150
+ for agent in agents
151
+ )
152
+ return _("Launch a sub-agent to handle complex tasks.\n\nAvailable agent types:\n{agent_list}").format(
153
+ agent_list=agent_list
154
+ )
142
155
 
143
156
  @property
144
157
  def input_schema(self) -> dict[str, Any]:
@@ -148,20 +161,20 @@ class AgentTool(Tool):
148
161
  "properties": {
149
162
  "prompt": {
150
163
  "type": "string",
151
- "description": "The task for the sub-agent to perform.",
164
+ "description": _("The task for the sub-agent to perform."),
152
165
  },
153
166
  "description": {
154
167
  "type": "string",
155
- "description": "Short (3-5 word) description of the task.",
168
+ "description": _("Short (3-5 word) description of the task."),
156
169
  },
157
170
  "subagent_type": {
158
171
  "type": "string",
159
172
  "enum": agent_types,
160
- "description": "The type of specialized agent to use.",
173
+ "description": _("The type of specialized agent to use."),
161
174
  },
162
175
  "run_in_background": {
163
176
  "type": "boolean",
164
- "description": "Run agent in background, parent continues.",
177
+ "description": _("Run agent in background, parent continues."),
165
178
  },
166
179
  },
167
180
  "required": ["prompt", "description"],
@@ -171,18 +184,30 @@ class AgentTool(Tool):
171
184
  prompt = tool_input["prompt"]
172
185
  agent_type = tool_input.get("subagent_type", tool_input.get("agent_type", "general-purpose"))
173
186
  run_in_background = tool_input.get("run_in_background", False)
187
+ event_queue = context.event_queue
174
188
 
175
189
  defn = get_agent_definition(agent_type)
176
190
  if defn is None:
177
- return ToolResult.error(f"Unknown agent type: '{agent_type}'")
191
+ return ToolResult.error(_("Unknown agent type: '{agent_type}'").format(agent_type=agent_type))
178
192
 
179
193
  if run_in_background and self._task_manager:
180
194
  task_id = self._task_manager.register(
181
- description=tool_input.get("description", "Sub-agent task"),
195
+ description=tool_input.get("description", _("Sub-agent task")),
182
196
  agent_type=agent_type,
183
197
  )
184
- asyncio.create_task(self._run_background(task_id, prompt, agent_type, context))
185
- return ToolResult.success(f"Background agent launched (task_id: {task_id}, type: {agent_type})")
198
+ background_task = asyncio.create_task(self._run_background(task_id, prompt, agent_type, context))
199
+ attach_task = getattr(self._task_manager, "attach_task", None)
200
+ if callable(attach_task):
201
+ attach_task(task_id, background_task)
202
+ background_task.add_done_callback(self._consume_background_task_exception)
203
+ if event_queue is not None:
204
+ await event_queue.put(None)
205
+ return ToolResult.success(
206
+ _("Background agent launched (task_id: {task_id}, type: {agent_type})").format(
207
+ task_id=task_id,
208
+ agent_type=agent_type,
209
+ )
210
+ )
186
211
 
187
212
  try:
188
213
  result_text, progress = await run_sub_agent(
@@ -192,18 +217,25 @@ class AgentTool(Tool):
192
217
  parent_provider_manager=self._provider_manager,
193
218
  parent_tool_registry=self._tool_registry,
194
219
  parent_system_prompt=self._system_prompt,
195
- event_queue=self._event_queue,
220
+ event_queue=event_queue,
196
221
  permission_context=self._permission_context,
197
222
  )
198
- if self._event_queue:
199
- await self._event_queue.put(None)
223
+ if event_queue is not None:
224
+ await event_queue.put(None)
200
225
  return ToolResult.success(
201
226
  f"{result_text}\n\n[Agent stats: {progress.tool_use_count} tool calls, {progress.token_count} tokens]"
202
227
  )
203
228
  except Exception as e:
204
- if self._event_queue:
205
- await self._event_queue.put(None)
206
- return ToolResult.error(f"Sub-agent failed: {e}")
229
+ if event_queue is not None:
230
+ await event_queue.put(None)
231
+ return ToolResult.error(_("Sub-agent failed: {error}").format(error=e))
232
+
233
+ @staticmethod
234
+ def _consume_background_task_exception(task: asyncio.Task) -> None:
235
+ if task.cancelled():
236
+ return
237
+ with suppress(asyncio.CancelledError):
238
+ task.exception()
207
239
 
208
240
  async def _run_background(
209
241
  self,
@@ -231,15 +263,37 @@ class AgentTool(Tool):
231
263
  if self._notification_queue:
232
264
  self._notification_queue.enqueue(
233
265
  task_id=task_id,
234
- message=f"Agent completed: {progress.tool_use_count} tool calls",
266
+ message=ngettext(
267
+ "Agent completed: {tool_count} tool call",
268
+ "Agent completed: {tool_count} tool calls",
269
+ progress.tool_use_count,
270
+ ).format(tool_count=progress.tool_use_count),
235
271
  )
272
+ except asyncio.CancelledError:
273
+ self._task_manager.stop(task_id)
274
+ if self._notification_queue:
275
+ self._notification_queue.enqueue(
276
+ task_id=task_id,
277
+ message=_("Agent stopped"),
278
+ )
279
+ raise
236
280
  except Exception as e:
237
- self._task_manager.fail(task_id, error=str(e))
281
+ error = str(e) or type(e).__name__
282
+ self._task_manager.fail(task_id, error=error)
283
+ if self._notification_queue:
284
+ self._notification_queue.enqueue(
285
+ task_id=task_id,
286
+ message=_("Agent failed: {error}").format(error=error),
287
+ )
288
+ except BaseException as e:
289
+ error = _format_base_exception(e)
290
+ self._task_manager.fail(task_id, error=error)
238
291
  if self._notification_queue:
239
292
  self._notification_queue.enqueue(
240
293
  task_id=task_id,
241
- message=f"Agent failed: {e}",
294
+ message=_("Agent failed: {error}").format(error=error),
242
295
  )
296
+ raise
243
297
 
244
298
  def is_read_only(self, input: dict | None = None) -> bool:
245
299
  return False
@@ -252,7 +306,7 @@ class AgentTool(Tool):
252
306
 
253
307
  def render_tool_result_message(self, output: str, *, is_error: bool = False, verbose: bool = False) -> str | None:
254
308
  if is_error:
255
- return f"Agent error: {output[:200]}"
309
+ return _("Agent error: {error}").format(error=output[:200])
256
310
  if verbose:
257
311
  return output
258
312
  # Extract stats from the end of the output
@@ -282,4 +336,4 @@ class AgentTool(Tool):
282
336
  def get_activity_description(self, input: dict | None = None) -> str | None:
283
337
  if input is None:
284
338
  return None
285
- return f"Running agent: {input.get('description', 'sub-agent')}"
339
+ return _("Running agent: {description}").format(description=input.get("description", _("sub-agent")))
@@ -5,6 +5,8 @@ from __future__ import annotations
5
5
  from dataclasses import dataclass, field
6
6
  from typing import TYPE_CHECKING
7
7
 
8
+ from iac_code.i18n import _
9
+
8
10
  if TYPE_CHECKING:
9
11
  from iac_code.tools.base import ToolRegistry
10
12
 
@@ -49,9 +51,9 @@ def get_builtin_agents() -> list[AgentDefinition]:
49
51
  return [
50
52
  AgentDefinition(
51
53
  agent_type="general-purpose",
52
- when_to_use=(
53
- "Use for complex, multi-step tasks that require research, "
54
- "code changes, or coordinating multiple operations."
54
+ when_to_use=_(
55
+ "Use for complex, multi-step tasks that require research, code changes, "
56
+ "or coordinating multiple operations."
55
57
  ),
56
58
  tools=["*"],
57
59
  disallowed_tools=["agent"],
@@ -59,9 +61,9 @@ def get_builtin_agents() -> list[AgentDefinition]:
59
61
  ),
60
62
  AgentDefinition(
61
63
  agent_type="explore",
62
- when_to_use=(
63
- "Use to quickly find files, search code, or answer questions "
64
- "about the codebase. Read-only cannot modify files."
64
+ when_to_use=_(
65
+ "Use to quickly find files, search code, or answer questions about the codebase. "
66
+ "Read-only; cannot modify files."
65
67
  ),
66
68
  tools=["read_file", "glob", "grep", "list_files", "bash"],
67
69
  disallowed_tools=["write_file", "edit_file", "agent"],
@@ -69,9 +71,9 @@ def get_builtin_agents() -> list[AgentDefinition]:
69
71
  ),
70
72
  AgentDefinition(
71
73
  agent_type="plan",
72
- when_to_use=(
73
- "Use to plan implementation strategy, review architecture, "
74
- "or design solutions. Read-only, no execution."
74
+ when_to_use=_(
75
+ "Use to plan implementation strategy, review architecture, or design solutions. "
76
+ "Read-only, no execution."
75
77
  ),
76
78
  tools=["read_file", "glob", "grep", "list_files"],
77
79
  disallowed_tools=["bash", "write_file", "edit_file", "agent"],
@@ -28,6 +28,10 @@ completion_init()
28
28
  # this works regardless of where it's called relative to `import typer` / click.
29
29
  setup_i18n()
30
30
 
31
+ # Import subcommands with Typer parameter help after i18n setup, because Typer
32
+ # captures custom option help text when the function signature is defined.
33
+ from iac_code.cli.update import update as _update_command # noqa: E402
34
+
31
35
  app = typer.Typer(
32
36
  name="iac-code",
33
37
  help=_("AI-powered infrastructure orchestration tool"),
@@ -54,6 +58,8 @@ if sys.platform == "win32":
54
58
  help=_("Install Git for Windows via the npmmirror mirror (Windows only)."),
55
59
  )(_install_git_bash)
56
60
 
61
+ app.command(name="update", help=_("Update iac-code to the latest version."))(_update_command)
62
+
57
63
 
58
64
  @a2a_client_app.callback()
59
65
  def a2a_client(
@@ -0,0 +1,38 @@
1
+ from __future__ import annotations
2
+
3
+ import shlex
4
+
5
+ import typer
6
+
7
+ from iac_code import __version__
8
+ from iac_code.i18n import _
9
+ from iac_code.services.update_checker import check_for_updates_once, run_update_command
10
+
11
+
12
+ def update(
13
+ check: bool = typer.Option(False, "--check", help=_("Check for updates without installing.")),
14
+ ) -> None:
15
+ """Update iac-code to the latest available version."""
16
+ state = check_for_updates_once(current_version=__version__, force=True)
17
+ pending = state.pending
18
+ if pending is None:
19
+ typer.echo(_("iac-code is already up to date (v{}).").format(__version__))
20
+ raise typer.Exit()
21
+
22
+ if check:
23
+ typer.echo(_("Update available: v{} -> v{}").format(pending.current_version, pending.version))
24
+ typer.echo(_("Run {} to update.").format(shlex.join(pending.update_command)))
25
+ raise typer.Exit()
26
+
27
+ typer.echo(_("Updating iac-code from v{} to v{}...").format(pending.current_version, pending.version))
28
+ try:
29
+ result = run_update_command(pending)
30
+ except OSError as exc:
31
+ typer.echo(_("Update command failed: {}").format(exc), err=True)
32
+ raise typer.Exit(1) from exc
33
+
34
+ if result.returncode != 0:
35
+ typer.echo(_("Update command failed with exit code {}.").format(result.returncode), err=True)
36
+ raise typer.Exit(result.returncode)
37
+
38
+ typer.echo(_("Successfully updated to v{}!").format(pending.version))
@@ -1400,6 +1400,8 @@ def _aliyun_oauth_login_flow(existing_cred: "AliyunCredential | None") -> str |
1400
1400
  return _BACK
1401
1401
  except AliyunOAuthError as exc:
1402
1402
  return _("Alibaba Cloud OAuth login failed: {error}").format(error=str(exc))
1403
+ finally:
1404
+ client.close()
1403
1405
 
1404
1406
  credential = AliyunCredential(
1405
1407
  mode="OAuth",