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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (308) hide show
  1. {iac_code-0.3.0 → iac_code-0.4.0}/PKG-INFO +15 -1
  2. {iac_code-0.3.0 → iac_code-0.4.0}/README.md +14 -0
  3. {iac_code-0.3.0 → iac_code-0.4.0}/pyproject.toml +3 -2
  4. iac_code-0.4.0/src/iac_code/__init__.py +2 -0
  5. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/client.py +45 -11
  6. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/transports/base.py +10 -2
  7. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/acp/server.py +135 -28
  8. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/acp/session.py +7 -1
  9. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/acp/slash_registry.py +42 -3
  10. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/agent/agent_loop.py +108 -0
  11. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/cli/headless.py +54 -0
  12. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/cli/main.py +38 -5
  13. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/cli/output_formats.py +6 -1
  14. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/commands/__init__.py +40 -2
  15. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/commands/auth.py +240 -8
  16. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/commands/clear.py +12 -2
  17. iac_code-0.4.0/src/iac_code/commands/memory.py +85 -0
  18. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/commands/registry.py +26 -4
  19. iac_code-0.4.0/src/iac_code/commands/rename.py +43 -0
  20. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/commands/resume.py +25 -4
  21. iac_code-0.4.0/src/iac_code/commands/skills.py +29 -0
  22. iac_code-0.4.0/src/iac_code/commands/status.py +98 -0
  23. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/config.py +28 -9
  24. iac_code-0.4.0/src/iac_code/i18n/locales/de/LC_MESSAGES/messages.mo +0 -0
  25. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/i18n/locales/de/LC_MESSAGES/messages.po +1000 -324
  26. iac_code-0.4.0/src/iac_code/i18n/locales/es/LC_MESSAGES/messages.mo +0 -0
  27. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/i18n/locales/es/LC_MESSAGES/messages.po +999 -324
  28. iac_code-0.4.0/src/iac_code/i18n/locales/fr/LC_MESSAGES/messages.mo +0 -0
  29. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/i18n/locales/fr/LC_MESSAGES/messages.po +998 -324
  30. iac_code-0.4.0/src/iac_code/i18n/locales/ja/LC_MESSAGES/messages.mo +0 -0
  31. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/i18n/locales/ja/LC_MESSAGES/messages.po +978 -324
  32. iac_code-0.4.0/src/iac_code/i18n/locales/pt/LC_MESSAGES/messages.mo +0 -0
  33. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/i18n/locales/pt/LC_MESSAGES/messages.po +995 -324
  34. iac_code-0.4.0/src/iac_code/i18n/locales/zh/LC_MESSAGES/messages.mo +0 -0
  35. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/i18n/locales/zh/LC_MESSAGES/messages.po +971 -324
  36. iac_code-0.4.0/src/iac_code/memory/memory_manager.py +182 -0
  37. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/memory/memory_tools.py +6 -1
  38. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/providers/manager.py +22 -0
  39. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/providers/registry.py +2 -1
  40. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/providers/thinking.py +1 -0
  41. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/agent_factory.py +7 -3
  42. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/permissions/loader.py +11 -5
  43. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/providers/aliyun.py +107 -5
  44. iac_code-0.4.0/src/iac_code/services/providers/aliyun_oauth.py +583 -0
  45. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/session_index.py +48 -24
  46. iac_code-0.4.0/src/iac_code/services/session_metadata.py +78 -0
  47. iac_code-0.4.0/src/iac_code/services/session_resolver.py +73 -0
  48. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/session_storage.py +124 -9
  49. iac_code-0.4.0/src/iac_code/services/session_usage.py +176 -0
  50. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/telemetry/client.py +1 -1
  51. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/telemetry/config.py +9 -0
  52. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/telemetry/fallback.py +4 -2
  53. iac_code-0.4.0/src/iac_code/services/update_checker.py +567 -0
  54. iac_code-0.4.0/src/iac_code/skills/auto_trigger.py +115 -0
  55. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/skills/bundled/__init__.py +2 -0
  56. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/skills/bundled/iac_aliyun/SKILL.md +13 -2
  57. iac_code-0.4.0/src/iac_code/skills/bundled/iac_aliyun/__init__.py +20 -0
  58. iac_code-0.4.0/src/iac_code/skills/bundled/iac_aliyun/auto_trigger.py +88 -0
  59. iac_code-0.4.0/src/iac_code/skills/bundled/iac_aliyun/references/template-parameter-recommendation.md +165 -0
  60. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/skills/discovery.py +42 -26
  61. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/skills/frontmatter.py +4 -0
  62. iac_code-0.4.0/src/iac_code/skills/management.py +81 -0
  63. iac_code-0.4.0/src/iac_code/skills/settings.py +61 -0
  64. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/skills/skill_definition.py +4 -0
  65. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/skills/skill_tool.py +13 -0
  66. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/base.py +13 -0
  67. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/cloud/aliyun/aliyun_api.py +23 -2
  68. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/cloud/aliyun/ros_client.py +14 -2
  69. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/cloud/aliyun/ros_stack_instances.py +3 -2
  70. iac_code-0.4.0/src/iac_code/tools/cloud/aliyun/user_agent.py +23 -0
  71. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/cloud/registry.py +10 -0
  72. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/edit_file.py +10 -2
  73. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/read_file.py +3 -0
  74. iac_code-0.4.0/src/iac_code/tools/result_storage.py +64 -0
  75. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/write_file.py +7 -1
  76. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/banner.py +52 -2
  77. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/core/input_history.py +36 -6
  78. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/core/prompt_input.py +18 -7
  79. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/core/raw_input.py +58 -0
  80. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/core/raw_input_win.py +51 -0
  81. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/dialogs/resume_picker.py +25 -2
  82. iac_code-0.4.0/src/iac_code/ui/dialogs/skills_picker.py +297 -0
  83. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/renderer.py +13 -2
  84. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/repl.py +493 -84
  85. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/spinner.py +0 -2
  86. iac_code-0.4.0/src/iac_code/ui/suggestions/command_provider.py +135 -0
  87. iac_code-0.4.0/src/iac_code/ui/suggestions/skill_provider.py +47 -0
  88. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/suggestions/token_extractor.py +32 -1
  89. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/suggestions/types.py +3 -3
  90. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/utils/file_security.py +14 -0
  91. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/utils/image/store.py +4 -2
  92. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/utils/json_utils.py +38 -0
  93. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/utils/log.py +6 -4
  94. iac_code-0.4.0/src/iac_code/utils/project_paths.py +176 -0
  95. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code.egg-info/PKG-INFO +15 -1
  96. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code.egg-info/SOURCES.txt +17 -0
  97. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code.egg-info/requires.txt +3 -2
  98. iac_code-0.3.0/src/iac_code/__init__.py +0 -2
  99. iac_code-0.3.0/src/iac_code/i18n/locales/de/LC_MESSAGES/messages.mo +0 -0
  100. iac_code-0.3.0/src/iac_code/i18n/locales/es/LC_MESSAGES/messages.mo +0 -0
  101. iac_code-0.3.0/src/iac_code/i18n/locales/fr/LC_MESSAGES/messages.mo +0 -0
  102. iac_code-0.3.0/src/iac_code/i18n/locales/ja/LC_MESSAGES/messages.mo +0 -0
  103. iac_code-0.3.0/src/iac_code/i18n/locales/pt/LC_MESSAGES/messages.mo +0 -0
  104. iac_code-0.3.0/src/iac_code/i18n/locales/zh/LC_MESSAGES/messages.mo +0 -0
  105. iac_code-0.3.0/src/iac_code/memory/memory_manager.py +0 -92
  106. iac_code-0.3.0/src/iac_code/skills/bundled/iac_aliyun/__init__.py +0 -16
  107. iac_code-0.3.0/src/iac_code/tools/result_storage.py +0 -39
  108. iac_code-0.3.0/src/iac_code/ui/suggestions/command_provider.py +0 -43
  109. iac_code-0.3.0/src/iac_code/utils/project_paths.py +0 -109
  110. {iac_code-0.3.0 → iac_code-0.4.0}/LICENSE +0 -0
  111. {iac_code-0.3.0 → iac_code-0.4.0}/MANIFEST.in +0 -0
  112. {iac_code-0.3.0 → iac_code-0.4.0}/setup.cfg +0 -0
  113. {iac_code-0.3.0 → iac_code-0.4.0}/setup.py +0 -0
  114. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/__init__.py +0 -0
  115. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/agent_card.py +0 -0
  116. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/app.py +0 -0
  117. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/artifacts.py +0 -0
  118. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/events.py +0 -0
  119. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/executor.py +0 -0
  120. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/exposure.py +0 -0
  121. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/metrics.py +0 -0
  122. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/parts.py +0 -0
  123. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/persistence.py +0 -0
  124. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/push.py +0 -0
  125. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/push_queue.py +0 -0
  126. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/push_secrets.py +0 -0
  127. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/push_worker.py +0 -0
  128. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/router.py +0 -0
  129. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/signing.py +0 -0
  130. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/task_store.py +0 -0
  131. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/transport.py +0 -0
  132. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/transports/__init__.py +0 -0
  133. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/transports/dispatcher.py +0 -0
  134. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/transports/grpc.py +0 -0
  135. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/transports/grpc_jsonrpc.py +0 -0
  136. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/transports/http.py +0 -0
  137. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/transports/redis_streams.py +0 -0
  138. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/transports/stdio.py +0 -0
  139. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/transports/unix.py +0 -0
  140. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/transports/websocket.py +0 -0
  141. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/a2a/types.py +0 -0
  142. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/acp/__init__.py +0 -0
  143. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/acp/convert.py +0 -0
  144. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/acp/http_sse.py +0 -0
  145. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/acp/mcp.py +0 -0
  146. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/acp/metrics.py +0 -0
  147. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/acp/state.py +0 -0
  148. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/acp/tools.py +0 -0
  149. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/acp/types.py +0 -0
  150. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/acp/version.py +0 -0
  151. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/agent/__init__.py +0 -0
  152. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/agent/agent_tool.py +0 -0
  153. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/agent/agent_types.py +0 -0
  154. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/agent/message.py +0 -0
  155. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/agent/system_prompt.py +0 -0
  156. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/cli/__init__.py +0 -0
  157. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/cli/install_git_bash.py +0 -0
  158. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/commands/compact.py +0 -0
  159. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/commands/debug.py +0 -0
  160. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/commands/effort.py +0 -0
  161. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/commands/exit.py +0 -0
  162. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/commands/help.py +0 -0
  163. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/commands/model.py +0 -0
  164. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/commands/tasks.py +0 -0
  165. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/i18n/__init__.py +0 -0
  166. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/memory/__init__.py +0 -0
  167. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/providers/__init__.py +0 -0
  168. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/providers/anthropic_provider.py +0 -0
  169. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/providers/azure_openai_provider.py +0 -0
  170. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/providers/base.py +0 -0
  171. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/providers/dashscope_provider.py +0 -0
  172. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/providers/deepseek_provider.py +0 -0
  173. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/providers/gemini_provider.py +0 -0
  174. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/providers/kimi_provider.py +0 -0
  175. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/providers/lmstudio_provider.py +0 -0
  176. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/providers/minimax_provider.py +0 -0
  177. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/providers/modelscope_provider.py +0 -0
  178. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/providers/ollama_provider.py +0 -0
  179. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/providers/openai_provider.py +0 -0
  180. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/providers/openrouter_provider.py +0 -0
  181. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/providers/retry.py +0 -0
  182. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/providers/siliconflow_provider.py +0 -0
  183. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/providers/stream_watchdog.py +0 -0
  184. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/providers/volcengine_provider.py +0 -0
  185. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/providers/zhipu_provider.py +0 -0
  186. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/__init__.py +0 -0
  187. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/capabilities/__init__.py +0 -0
  188. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/capabilities/auto_detect.py +0 -0
  189. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/capabilities/multimodal.py +0 -0
  190. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/cloud_credentials.py +0 -0
  191. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/context_manager.py +0 -0
  192. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/permissions/__init__.py +0 -0
  193. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/permissions/pipeline.py +0 -0
  194. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/permissions/storage.py +0 -0
  195. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/providers/__init__.py +0 -0
  196. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/qwenpaw_source.py +0 -0
  197. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/telemetry/__init__.py +0 -0
  198. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/telemetry/attributes.py +0 -0
  199. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/telemetry/constants.py +0 -0
  200. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/telemetry/content_serializer.py +0 -0
  201. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/telemetry/events.py +0 -0
  202. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/telemetry/identity.py +0 -0
  203. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/telemetry/metrics.py +0 -0
  204. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/telemetry/names.py +0 -0
  205. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/telemetry/sanitize.py +0 -0
  206. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/telemetry/sink.py +0 -0
  207. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/telemetry/tracing.py +0 -0
  208. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/telemetry/types.py +0 -0
  209. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/token_budget.py +0 -0
  210. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/services/token_counter.py +0 -0
  211. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/skills/__init__.py +0 -0
  212. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/ecs.md +0 -0
  213. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/oss.md +0 -0
  214. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/rds.md +0 -0
  215. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/redis.md +0 -0
  216. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/slb.md +0 -0
  217. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/vpc.md +0 -0
  218. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/skills/bundled/iac_aliyun/references/ros-template.md +0 -0
  219. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/skills/bundled/iac_aliyun/references/template-parameters.md +0 -0
  220. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/skills/bundled/iac_aliyun/references/terraform-template.md +0 -0
  221. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/skills/bundled/iac_aliyun/scripts/tf2ros.py +0 -0
  222. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/skills/bundled/simplify.py +0 -0
  223. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/skills/listing.py +0 -0
  224. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/skills/loader.py +0 -0
  225. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/skills/processor.py +0 -0
  226. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/skills/renderer.py +0 -0
  227. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/state/__init__.py +0 -0
  228. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/state/app_state.py +0 -0
  229. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tasks/__init__.py +0 -0
  230. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tasks/notification_queue.py +0 -0
  231. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tasks/task_state.py +0 -0
  232. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tasks/task_tools.py +0 -0
  233. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/__init__.py +0 -0
  234. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/bash/__init__.py +0 -0
  235. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/bash/bash_tool.py +0 -0
  236. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/bash/command_parser.py +0 -0
  237. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/bash/mode_validation.py +0 -0
  238. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/bash/path_validation.py +0 -0
  239. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/bash/permissions.py +0 -0
  240. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/bash/readonly_commands.py +0 -0
  241. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/bash/rule_matching.py +0 -0
  242. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/bash/safety_checks.py +0 -0
  243. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/cloud/__init__.py +0 -0
  244. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/cloud/aliyun/__init__.py +0 -0
  245. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/cloud/aliyun/aliyun_doc_search.py +0 -0
  246. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/cloud/aliyun/api_hooks.py +0 -0
  247. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/cloud/aliyun/endpoints.yml +0 -0
  248. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/cloud/aliyun/hooks/__init__.py +0 -0
  249. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/cloud/aliyun/hooks/ros_parameters.py +0 -0
  250. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/cloud/aliyun/hooks/ros_validate.py +0 -0
  251. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/cloud/aliyun/ros_stack.py +0 -0
  252. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/cloud/aliyun/ros_yaml.py +0 -0
  253. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/cloud/base_api.py +0 -0
  254. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/cloud/base_stack.py +0 -0
  255. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/cloud/types.py +0 -0
  256. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/glob.py +0 -0
  257. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/grep.py +0 -0
  258. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/list_files.py +0 -0
  259. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/tool_executor.py +0 -0
  260. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/tools/web_fetch.py +0 -0
  261. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/types/__init__.py +0 -0
  262. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/types/permissions.py +0 -0
  263. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/types/skill_source.py +0 -0
  264. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/types/stream_events.py +0 -0
  265. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/__init__.py +0 -0
  266. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/components/__init__.py +0 -0
  267. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/components/dialog.py +0 -0
  268. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/components/divider.py +0 -0
  269. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/components/fuzzy_picker.py +0 -0
  270. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/components/progress_bar.py +0 -0
  271. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/components/search_box.py +0 -0
  272. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/components/select.py +0 -0
  273. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/components/status_icon.py +0 -0
  274. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/components/tabs.py +0 -0
  275. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/core/__init__.py +0 -0
  276. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/core/in_place_render.py +0 -0
  277. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/core/key_event.py +0 -0
  278. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/core/screen.py +0 -0
  279. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/dialogs/__init__.py +0 -0
  280. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/dialogs/global_search.py +0 -0
  281. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/dialogs/history_search.py +0 -0
  282. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/dialogs/model_picker.py +0 -0
  283. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/dialogs/quick_open.py +0 -0
  284. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/keybindings/__init__.py +0 -0
  285. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/keybindings/manager.py +0 -0
  286. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/suggestions/__init__.py +0 -0
  287. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/suggestions/aggregator.py +0 -0
  288. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/suggestions/directory_provider.py +0 -0
  289. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/suggestions/file_provider.py +0 -0
  290. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/suggestions/shell_history_provider.py +0 -0
  291. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/ui/transcript_view.py +0 -0
  292. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/utils/__init__.py +0 -0
  293. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/utils/background_housekeeping.py +0 -0
  294. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/utils/cleanup.py +0 -0
  295. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/utils/console.py +0 -0
  296. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/utils/image/__init__.py +0 -0
  297. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/utils/image/clipboard.py +0 -0
  298. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/utils/image/format_detect.py +0 -0
  299. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/utils/image/pasted_content.py +0 -0
  300. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/utils/image/processor.py +0 -0
  301. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/utils/image/resizer.py +0 -0
  302. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/utils/platform.py +0 -0
  303. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/utils/signals.py +0 -0
  304. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/utils/tool_input_parser.py +0 -0
  305. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code/utils/windows_paths.py +0 -0
  306. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code.egg-info/dependency_links.txt +0 -0
  307. {iac_code-0.3.0 → iac_code-0.4.0}/src/iac_code.egg-info/entry_points.txt +0 -0
  308. {iac_code-0.3.0 → iac_code-0.4.0}/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.3.0
3
+ Version: 0.4.0
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
@@ -74,6 +74,20 @@ Reading from stdin is also supported:
74
74
  echo "Create an OSS Bucket" | iac-code --prompt -
75
75
  ```
76
76
 
77
+ ## Contributing
78
+
79
+ Install [uv](https://docs.astral.sh/uv/getting-started/installation/), then:
80
+
81
+ ```bash
82
+ make install # install dependencies and pre-commit hooks
83
+ make dev # run in debug mode
84
+ make test # run tests
85
+ make lint # run linters
86
+ make format # format code
87
+ ```
88
+
89
+ See the [Contributing Guide](https://aliyun.github.io/iac-code/getting-started/contributing) for details.
90
+
77
91
  ## Contact Us
78
92
 
79
93
  | [DingTalk](https://qr.dingtalk.com/action/joingroup?code=v1,k1,ubm/77U7qRh/STFZUNBP26X4PNg2z6+uhiPcLGtDNfU=&_dt_no_comment=1&origin=11) | [Discord](https://discord.gg/qECFuFBwF) |
@@ -55,6 +55,20 @@ Reading from stdin is also supported:
55
55
  echo "Create an OSS Bucket" | iac-code --prompt -
56
56
  ```
57
57
 
58
+ ## Contributing
59
+
60
+ Install [uv](https://docs.astral.sh/uv/getting-started/installation/), then:
61
+
62
+ ```bash
63
+ make install # install dependencies and pre-commit hooks
64
+ make dev # run in debug mode
65
+ make test # run tests
66
+ make lint # run linters
67
+ make format # format code
68
+ ```
69
+
70
+ See the [Contributing Guide](https://aliyun.github.io/iac-code/getting-started/contributing) for details.
71
+
58
72
  ## Contact Us
59
73
 
60
74
  | [DingTalk](https://qr.dingtalk.com/action/joingroup?code=v1,k1,ubm/77U7qRh/STFZUNBP26X4PNg2z6+uhiPcLGtDNfU=&_dt_no_comment=1&origin=11) | [Discord](https://discord.gg/qECFuFBwF) |
@@ -21,6 +21,7 @@ dependencies = [
21
21
  "pyperclip>=1.8.0",
22
22
  "openai>=1.50",
23
23
  "httpx>=0.27.0",
24
+ "packaging>=24.0",
24
25
  "tiktoken>=0.7.0",
25
26
  "jsonschema>=4.20",
26
27
  "alibabacloud-ros20190910>=3.0.0",
@@ -32,8 +33,8 @@ dependencies = [
32
33
  "pillow==12.2.0",
33
34
  "cryptography>=42.0",
34
35
  "keyring>=25.0",
35
- "tree-sitter>=0.23",
36
- "tree-sitter-bash>=0.23",
36
+ "tree-sitter>=0.25,<0.26",
37
+ "tree-sitter-bash>=0.25,<0.26",
37
38
  ]
38
39
 
39
40
  [project.optional-dependencies]
@@ -0,0 +1,2 @@
1
+ __version__ = "0.4.0"
2
+ __release_date__ = "2026-06-03"
@@ -7,6 +7,7 @@ from time import monotonic
7
7
  from typing import Any, AsyncIterator
8
8
 
9
9
  import httpx
10
+ from a2a.types import Role
10
11
 
11
12
  from iac_code.a2a.signing import AgentCardSignature, agent_card_signature_jwks_url, verify_agent_card_dict
12
13
  from iac_code.a2a.transport import A2AAuthConfig, A2ATransportBinding, UnsupportedA2ATransportError, headers_for_auth
@@ -31,19 +32,52 @@ class A2AClientResponse:
31
32
  if not isinstance(result, dict):
32
33
  return ""
33
34
  text = result.get("text")
34
- if isinstance(text, str):
35
+ if isinstance(text, str) and text:
35
36
  return text
36
37
  status = result.get("status")
37
- if not isinstance(status, dict):
38
- return ""
39
- message = status.get("message")
40
- if not isinstance(message, dict):
41
- return ""
42
- parts = message.get("parts")
43
- if not isinstance(parts, list) or not parts or not isinstance(parts[0], dict):
44
- return ""
45
- value = parts[0].get("text")
46
- return value if isinstance(value, str) else ""
38
+ if isinstance(status, dict):
39
+ extracted = _extract_parts_text(status.get("message"))
40
+ if extracted:
41
+ return extracted
42
+ task = result.get("task")
43
+ if isinstance(task, dict):
44
+ task_status = task.get("status")
45
+ if isinstance(task_status, dict):
46
+ extracted = _extract_parts_text(task_status.get("message"))
47
+ if extracted:
48
+ return extracted
49
+ history = task.get("history")
50
+ if isinstance(history, list):
51
+ for entry in reversed(history):
52
+ extracted = _extract_agent_entry_text(entry)
53
+ if extracted:
54
+ return extracted
55
+ return ""
56
+
57
+
58
+ _AGENT_ROLE_NAME = Role.Name(Role.ROLE_AGENT)
59
+
60
+
61
+ def _extract_agent_entry_text(entry: Any) -> str:
62
+ if not isinstance(entry, dict) or entry.get("role") != _AGENT_ROLE_NAME:
63
+ return ""
64
+ return _extract_parts_text(entry)
65
+
66
+
67
+ def _extract_parts_text(message: Any) -> str:
68
+ if not isinstance(message, dict):
69
+ return ""
70
+ parts = message.get("parts")
71
+ if not isinstance(parts, list):
72
+ return ""
73
+ pieces: list[str] = []
74
+ for part in parts:
75
+ if not isinstance(part, dict):
76
+ continue
77
+ value = part.get("text")
78
+ if isinstance(value, str):
79
+ pieces.append(value)
80
+ return "".join(pieces)
47
81
 
48
82
 
49
83
  class A2AClient:
@@ -9,7 +9,7 @@ from urllib.parse import urlparse
9
9
  from iac_code.a2a.transport import A2ATransportBinding, UnsupportedA2ATransportError
10
10
  from iac_code.i18n import _
11
11
 
12
- RUNNABLE_TRANSPORTS = frozenset({"http", "stdio", "unix", "websocket", "grpc", "grpc-jsonrpc", "redis-streams"})
12
+ SUPPORTED_TRANSPORTS = frozenset({"http", "stdio", "unix", "websocket", "grpc", "grpc-jsonrpc", "redis-streams"})
13
13
 
14
14
 
15
15
  class A2ATransportConfigError(ValueError):
@@ -159,8 +159,16 @@ def select_binding(bindings: Sequence[A2ATransportBinding]) -> A2ATransportBindi
159
159
  raise UnsupportedA2ATransportError(f"No runnable A2A transport found. Candidate bindings: {names}")
160
160
 
161
161
 
162
+ def validate_transport_supported(transport: str) -> None:
163
+ """Raise ValueError if the transport name is not recognised."""
164
+ if transport not in SUPPORTED_TRANSPORTS:
165
+ supported = ", ".join(sorted(SUPPORTED_TRANSPORTS))
166
+ raise ValueError(f"Unsupported transport '{transport}'. Supported values: {supported}")
167
+
168
+
162
169
  def validate_transport_for_platform(transport: str) -> None:
163
- """Raise RuntimeError if the transport is unavailable on the current platform."""
170
+ """Raise an error if the transport is unsupported or unavailable on the current platform."""
171
+ validate_transport_supported(transport)
164
172
  if transport == "unix" and sys.platform == "win32":
165
173
  raise RuntimeError(
166
174
  _(
@@ -18,8 +18,12 @@ from iac_code.acp.types import ACPContentBlock, MCPServer
18
18
  from iac_code.acp.version import negotiate_version
19
19
  from iac_code.commands import LocalCommand, create_default_registry
20
20
  from iac_code.config import DEFAULT_MODEL, get_active_provider_key, load_saved_model
21
+ from iac_code.i18n import _
21
22
  from iac_code.services.agent_factory import AgentFactoryOptions, create_agent_runtime
23
+ from iac_code.services.session_index import SessionEntry, SessionIndex
24
+ from iac_code.services.session_resolver import ResolutionStatus, resolve_session_argument
22
25
  from iac_code.services.session_storage import SessionStorage
26
+ from iac_code.utils.project_paths import format_resume_command, same_project_path
23
27
 
24
28
  SESSION_IDLE_TIMEOUT = 3600 # 1 hour
25
29
  CLEANUP_INTERVAL = 300 # 5 minutes
@@ -152,7 +156,12 @@ class ACPServer:
152
156
  runtime.session_id,
153
157
  )
154
158
  session = ACPSession(
155
- runtime.session_id, runtime.agent_loop, self.conn, mcp_configs=mcp_configs, metrics=self.metrics
159
+ runtime.session_id,
160
+ runtime.agent_loop,
161
+ self.conn,
162
+ mcp_configs=mcp_configs,
163
+ metrics=self.metrics,
164
+ memory_manager=getattr(runtime, "memory_manager", None),
156
165
  )
157
166
  self.sessions[session.id] = session
158
167
  self.metrics.record_session_created()
@@ -228,25 +237,16 @@ class ACPServer:
228
237
  cwd: str | None = None,
229
238
  **kwargs: Any,
230
239
  ) -> acp.schema.ListSessionsResponse:
231
- from iac_code.utils.project_paths import get_project_dir, get_projects_dir
232
-
233
- session_ids: list[str] = []
234
- if cwd:
235
- project_dir = get_project_dir(cwd)
236
- if project_dir.exists():
237
- session_ids = [p.stem for p in project_dir.glob("*.jsonl")]
238
- else:
239
- projects_root = get_projects_dir()
240
- if projects_root.exists():
241
- session_ids = [p.stem for p in projects_root.glob("*/*.jsonl")]
240
+ index = SessionIndex()
241
+ entries = index.list_for_cwd(cwd) if cwd else index.list_all_projects()
242
242
  return acp.schema.ListSessionsResponse(
243
243
  sessions=[
244
244
  acp.schema.SessionInfo(
245
- session_id=session_id,
246
- cwd=cwd or "",
247
- title=session_id,
245
+ session_id=entry.session_id,
246
+ cwd=entry.cwd or cwd or "",
247
+ title=entry.title,
248
248
  )
249
- for session_id in session_ids
249
+ for entry in entries
250
250
  ],
251
251
  next_cursor=None,
252
252
  )
@@ -297,7 +297,14 @@ class ACPServer:
297
297
  runtime.agent_loop.context_manager.load_messages(history)
298
298
 
299
299
  # 4. Register session
300
- session = ACPSession(session_id, runtime.agent_loop, self.conn, mcp_configs=mcp_configs, metrics=self.metrics)
300
+ session = ACPSession(
301
+ session_id,
302
+ runtime.agent_loop,
303
+ self.conn,
304
+ mcp_configs=mcp_configs,
305
+ metrics=self.metrics,
306
+ memory_manager=getattr(runtime, "memory_manager", None),
307
+ )
301
308
  self.sessions[session_id] = session
302
309
  self.metrics.record_session_created()
303
310
  logger.info("Session loaded, session_id=%s, history_messages=%d", session_id, len(history))
@@ -360,7 +367,12 @@ class ACPServer:
360
367
 
361
368
  # 4. Register the forked session
362
369
  session = ACPSession(
363
- new_session_id, runtime.agent_loop, self.conn, mcp_configs=mcp_configs, metrics=self.metrics
370
+ new_session_id,
371
+ runtime.agent_loop,
372
+ self.conn,
373
+ mcp_configs=mcp_configs,
374
+ metrics=self.metrics,
375
+ memory_manager=getattr(runtime, "memory_manager", None),
364
376
  )
365
377
  self.sessions[new_session_id] = session
366
378
  self.metrics.record_session_created()
@@ -384,20 +396,66 @@ class ACPServer:
384
396
  mcp_servers: list[MCPServer] | None = None,
385
397
  **kwargs: Any,
386
398
  ) -> acp.schema.ResumeSessionResponse:
387
- # 1. If session is still active in memory, return directly
388
- if session_id in self.sessions:
399
+ # 1. If session is still active in memory by exact id, enforce project ownership before returning.
400
+ active_session = self.sessions.get(session_id)
401
+ if active_session is not None:
402
+ error = _active_session_project_error(cwd, session_id, session_id, active_session)
403
+ if error is not None:
404
+ raise error
389
405
  await self._push_available_commands(session_id)
390
406
  return acp.schema.ResumeSessionResponse()
391
407
 
392
408
  if self.conn is None:
393
409
  raise acp.RequestError.internal_error({"error": "ACP client not connected"})
394
410
 
395
- # 2. Try to load persisted history from SessionStorage
411
+ resolution = resolve_session_argument(SessionIndex(), cwd, session_id)
412
+ if resolution.status == ResolutionStatus.NOT_FOUND:
413
+ raise _invalid_params(_("Session not found"), {"session_id": session_id})
414
+ if resolution.status == ResolutionStatus.AMBIGUOUS_NAME:
415
+ candidate_ids = [entry.session_id for entry in resolution.candidates]
416
+ message = _("Session name is ambiguous. Candidates: {candidates}").format(
417
+ candidates=", ".join(candidate_ids)
418
+ )
419
+ raise _invalid_params(
420
+ message,
421
+ {
422
+ "session_id": session_id,
423
+ "candidates": [_resume_candidate_data(entry) for entry in resolution.candidates],
424
+ },
425
+ )
426
+
427
+ entry = resolution.entry
428
+ if entry is None: # pragma: no cover - defensive guard for inconsistent resolver output
429
+ raise _invalid_params(_("Session not found"), {"session_id": session_id})
430
+
431
+ resolved_session_id = entry.session_id
432
+ if entry.cwd and not same_project_path(entry.cwd, cwd):
433
+ hint = _resume_command(entry.cwd, resolved_session_id)
434
+ message = _("Session belongs to another project. Run: {hint}").format(hint=hint)
435
+ raise _invalid_params(
436
+ message,
437
+ {
438
+ "session_id": session_id,
439
+ "resolved_session_id": resolved_session_id,
440
+ "cwd": entry.cwd,
441
+ "hint": hint,
442
+ },
443
+ )
444
+
445
+ active_session = self.sessions.get(resolved_session_id)
446
+ if active_session is not None:
447
+ error = _active_session_project_error(cwd, session_id, resolved_session_id, active_session)
448
+ if error is not None:
449
+ raise error
450
+ await self._push_available_commands(resolved_session_id)
451
+ return acp.schema.ResumeSessionResponse()
452
+
453
+ # 2. Try to load persisted history from SessionStorage.
396
454
  storage = SessionStorage()
397
- if not storage.exists(cwd, session_id):
398
- raise acp.RequestError.invalid_params({"session_id": "Session not found"})
455
+ if not storage.exists(cwd, resolved_session_id):
456
+ raise _invalid_params(_("Session not found"), {"session_id": session_id})
399
457
 
400
- history = storage.load(cwd, session_id)
458
+ history = storage.load(cwd, resolved_session_id)
401
459
  history = SessionStorage.repair_interrupted(history)
402
460
 
403
461
  # Convert MCP server configs from ACP protocol types to internal dicts
@@ -405,7 +463,7 @@ class ACPServer:
405
463
 
406
464
  # 3. Rebuild agent runtime with restored history
407
465
  model = load_saved_model() or DEFAULT_MODEL
408
- runtime = self._create_runtime_with_auth_check(model=model, session_id=session_id, cwd=cwd)
466
+ runtime = self._create_runtime_with_auth_check(model=model, session_id=resolved_session_id, cwd=cwd)
409
467
  replace_bash_with_acp_terminal(
410
468
  runtime.tool_registry,
411
469
  self.client_capabilities,
@@ -418,10 +476,17 @@ class ACPServer:
418
476
  runtime.agent_loop.context_manager.load_messages(history)
419
477
 
420
478
  # 4. Register the resumed session
421
- session = ACPSession(session_id, runtime.agent_loop, self.conn, mcp_configs=mcp_configs, metrics=self.metrics)
422
- self.sessions[session_id] = session
479
+ session = ACPSession(
480
+ resolved_session_id,
481
+ runtime.agent_loop,
482
+ self.conn,
483
+ mcp_configs=mcp_configs,
484
+ metrics=self.metrics,
485
+ memory_manager=getattr(runtime, "memory_manager", None),
486
+ )
487
+ self.sessions[resolved_session_id] = session
423
488
  self.metrics.record_session_created()
424
- await self._push_available_commands(session_id)
489
+ await self._push_available_commands(resolved_session_id)
425
490
 
426
491
  return acp.schema.ResumeSessionResponse()
427
492
 
@@ -619,6 +684,48 @@ def _convert_mcp_servers(mcp_servers: list[MCPServer] | None) -> list[dict[str,
619
684
  return configs
620
685
 
621
686
 
687
+ def _invalid_params(message: str, data: dict[str, Any] | None = None) -> acp.RequestError:
688
+ """Create an ACP invalid-params error with a useful message."""
689
+ return acp.RequestError(-32602, message, data)
690
+
691
+
692
+ def _resume_command(cwd: str, session_id: str) -> str:
693
+ return format_resume_command(cwd, session_id)
694
+
695
+
696
+ def _active_session_cwd(session: ACPSession) -> str | None:
697
+ cwd = getattr(session.agent_loop, "_cwd", None)
698
+ return cwd if isinstance(cwd, str) and cwd else None
699
+
700
+
701
+ def _active_session_project_error(
702
+ cwd: str, session_id: str, resolved_session_id: str, session: ACPSession
703
+ ) -> acp.RequestError | None:
704
+ active_cwd = _active_session_cwd(session)
705
+ if not active_cwd or same_project_path(active_cwd, cwd):
706
+ return None
707
+ hint = _resume_command(active_cwd, resolved_session_id)
708
+ message = _("Session belongs to another project. Run: {hint}").format(hint=hint)
709
+ return _invalid_params(
710
+ message,
711
+ {
712
+ "session_id": session_id,
713
+ "resolved_session_id": resolved_session_id,
714
+ "cwd": active_cwd,
715
+ "hint": hint,
716
+ },
717
+ )
718
+
719
+
720
+ def _resume_candidate_data(entry: SessionEntry) -> dict[str, str | None]:
721
+ return {
722
+ "session_id": entry.session_id,
723
+ "name": entry.name,
724
+ "cwd": entry.cwd,
725
+ "command": _resume_command(entry.cwd, entry.session_id),
726
+ }
727
+
728
+
622
729
  # ---------------------------------------------------------------------------
623
730
  # Auth methods declaration
624
731
  # ---------------------------------------------------------------------------
@@ -165,9 +165,11 @@ class ACPSession:
165
165
  conn: acp.Client,
166
166
  mcp_configs: list[dict] | None = None,
167
167
  metrics: ACPMetrics | None = None,
168
+ memory_manager=None,
168
169
  ) -> None:
169
170
  self.id = session_id
170
171
  self.agent_loop = agent_loop
172
+ self.memory_manager = memory_manager
171
173
  self._conn = conn
172
174
  self._current_task: asyncio.Task | None = None
173
175
  self._replay_task: asyncio.Task[None] | None = None
@@ -292,7 +294,11 @@ class ACPSession:
292
294
  prompt_text = acp_blocks_to_prompt_text(prompt)
293
295
  slash_registry = ACPSlashRegistry()
294
296
  if slash_registry.is_slash_command(prompt_text):
295
- result = await slash_registry.execute(prompt_text, self.agent_loop)
297
+ result = await slash_registry.execute(
298
+ prompt_text,
299
+ self.agent_loop,
300
+ memory_manager=self.memory_manager,
301
+ )
296
302
  await self._conn.session_update(
297
303
  session_id=self.id,
298
304
  update=acp.schema.AgentMessageChunk(
@@ -1,7 +1,7 @@
1
1
  """ACP slash command registry.
2
2
 
3
3
  Manages commands supported over the ACP protocol.
4
- Only /compact, /clear, and /debug are allowed;
4
+ Only /compact, /clear, /debug, /memory, and /rename are allowed;
5
5
  all other slash commands are rejected with a clear message.
6
6
  """
7
7
 
@@ -10,10 +10,12 @@ from __future__ import annotations
10
10
  import logging
11
11
 
12
12
  from iac_code.i18n import _
13
+ from iac_code.services.session_metadata import normalize_session_name
14
+ from iac_code.services.session_storage import SessionStorage
13
15
 
14
16
  logger = logging.getLogger(__name__)
15
17
 
16
- ACP_SUPPORTED_COMMANDS: frozenset[str] = frozenset({"compact", "clear", "debug"})
18
+ ACP_SUPPORTED_COMMANDS: frozenset[str] = frozenset({"compact", "clear", "debug", "memory", "rename"})
17
19
 
18
20
 
19
21
  class ACPSlashRegistry:
@@ -51,6 +53,10 @@ class ACPSlashRegistry:
51
53
  return await self._handle_clear(agent_loop)
52
54
  if cmd_name == "debug":
53
55
  return self._handle_debug(args_str)
56
+ if cmd_name == "memory":
57
+ return self._handle_memory(args_str, context.get("memory_manager"))
58
+ if cmd_name == "rename":
59
+ return self._handle_rename(args_str, agent_loop)
54
60
 
55
61
  # Should not reach here
56
62
  return _("Command '/{cmd_name}' handler not implemented.").format(cmd_name=cmd_name) # pragma: no cover
@@ -91,7 +97,7 @@ class ACPSlashRegistry:
91
97
  async def _handle_clear(self, agent_loop) -> str:
92
98
  """Clear the agent_loop conversation history."""
93
99
  try:
94
- agent_loop.context_manager.reset()
100
+ agent_loop.reset()
95
101
  except Exception as exc:
96
102
  logger.warning("ACP /clear failed: %s", exc)
97
103
  return _("Clear failed: {error}").format(error=exc)
@@ -123,3 +129,36 @@ class ACPSlashRegistry:
123
129
  return _("Debug logging disabled.")
124
130
 
125
131
  return _("Usage: /debug [on|off]")
132
+
133
+ def _handle_memory(self, args: str, memory_manager) -> str:
134
+ """View and manage persistent memories."""
135
+ if memory_manager is None:
136
+ return _("Memory manager is unavailable.")
137
+
138
+ from iac_code.commands.memory import execute_memory_command
139
+
140
+ return execute_memory_command(memory_manager, args.split())
141
+
142
+ def _handle_rename(self, args: str, agent_loop) -> str:
143
+ """Rename the current ACP session non-interactively."""
144
+ parts = args.split()
145
+ if len(parts) != 1:
146
+ return _("Usage: /rename <name>")
147
+
148
+ cwd = getattr(agent_loop, "_cwd", None)
149
+ session_id = getattr(agent_loop, "_session_id", None)
150
+ git_branch = getattr(agent_loop, "_current_git_branch", None)
151
+ if not isinstance(cwd, str) or not isinstance(session_id, str):
152
+ return _("Rename is only available after a session is created.")
153
+ if not isinstance(git_branch, str):
154
+ git_branch = None
155
+
156
+ try:
157
+ name = normalize_session_name(parts[0])
158
+ result = SessionStorage().rename_session(cwd, session_id, name, git_branch=git_branch)
159
+ except ValueError as exc:
160
+ return str(exc)
161
+
162
+ if result == "unchanged":
163
+ return _("Session is already named {name}").format(name=name)
164
+ return _("Renamed session to {name}").format(name=name)