iac-code 0.3.1__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.
- {iac_code-0.3.1 → iac_code-0.4.1}/PKG-INFO +1 -1
- iac_code-0.4.1/src/iac_code/__init__.py +2 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/executor.py +18 -2
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/acp/server.py +135 -28
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/acp/session.py +22 -2
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/acp/slash_registry.py +41 -2
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/acp/tools.py +39 -4
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/agent/agent_loop.py +121 -8
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/agent/agent_tool.py +79 -25
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/agent/agent_types.py +11 -9
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/cli/main.py +7 -1
- iac_code-0.4.1/src/iac_code/cli/update.py +38 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/commands/__init__.py +40 -2
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/commands/auth.py +242 -8
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/commands/clear.py +11 -1
- iac_code-0.4.1/src/iac_code/commands/memory.py +85 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/commands/model.py +25 -10
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/commands/registry.py +18 -0
- iac_code-0.4.1/src/iac_code/commands/rename.py +43 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/commands/resume.py +25 -4
- iac_code-0.4.1/src/iac_code/commands/skills.py +29 -0
- iac_code-0.4.1/src/iac_code/commands/status.py +104 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/commands/tasks.py +2 -1
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/config.py +3 -2
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/i18n/__init__.py +13 -1
- iac_code-0.4.1/src/iac_code/i18n/locales/de/LC_MESSAGES/messages.mo +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/i18n/locales/de/LC_MESSAGES/messages.po +1304 -568
- iac_code-0.4.1/src/iac_code/i18n/locales/es/LC_MESSAGES/messages.mo +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/i18n/locales/es/LC_MESSAGES/messages.po +1302 -568
- iac_code-0.4.1/src/iac_code/i18n/locales/fr/LC_MESSAGES/messages.mo +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/i18n/locales/fr/LC_MESSAGES/messages.po +1306 -568
- iac_code-0.4.1/src/iac_code/i18n/locales/ja/LC_MESSAGES/messages.mo +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/i18n/locales/ja/LC_MESSAGES/messages.po +1276 -568
- iac_code-0.4.1/src/iac_code/i18n/locales/pt/LC_MESSAGES/messages.mo +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/i18n/locales/pt/LC_MESSAGES/messages.po +1300 -568
- iac_code-0.4.1/src/iac_code/i18n/locales/zh/LC_MESSAGES/messages.mo +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/i18n/locales/zh/LC_MESSAGES/messages.po +1269 -568
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/memory/memory_manager.py +71 -16
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/memory/memory_tools.py +8 -3
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/providers/manager.py +104 -27
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/providers/openai_provider.py +9 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/providers/openrouter_provider.py +12 -9
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/providers/registry.py +1 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/providers/thinking.py +1 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/agent_factory.py +8 -3
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/context_manager.py +51 -3
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/permissions/loader.py +1 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/permissions/pipeline.py +32 -2
- iac_code-0.4.1/src/iac_code/services/permissions/trusted_roots.py +21 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/providers/aliyun.py +131 -6
- iac_code-0.4.1/src/iac_code/services/providers/aliyun_oauth.py +599 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/session_index.py +74 -25
- iac_code-0.4.1/src/iac_code/services/session_metadata.py +78 -0
- iac_code-0.4.1/src/iac_code/services/session_resolver.py +73 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/session_storage.py +116 -5
- iac_code-0.4.1/src/iac_code/services/session_usage.py +176 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/telemetry/__init__.py +2 -1
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/telemetry/identity.py +27 -1
- iac_code-0.4.1/src/iac_code/services/token_counter.py +149 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/update_checker.py +8 -3
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/SKILL.md +9 -0
- iac_code-0.4.1/src/iac_code/skills/bundled/iac_aliyun/references/template-parameter-recommendation.md +165 -0
- iac_code-0.4.1/src/iac_code/skills/management.py +81 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/skills/renderer.py +119 -30
- iac_code-0.4.1/src/iac_code/skills/settings.py +61 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/skills/skill_tool.py +16 -1
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tasks/task_state.py +20 -5
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tasks/task_tools.py +2 -1
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/base.py +4 -0
- iac_code-0.4.1/src/iac_code/tools/bash/argv_safety.py +1112 -0
- iac_code-0.4.1/src/iac_code/tools/bash/path_validation.py +249 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/bash/permissions.py +33 -28
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/bash/readonly_commands.py +9 -24
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/bash/safety_checks.py +10 -53
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/aliyun_api.py +18 -2
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/ros_client.py +9 -2
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/ros_stack.py +313 -139
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/ros_stack_instances.py +3 -2
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/cloud/base_stack.py +111 -49
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/cloud/registry.py +10 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/cloud/types.py +10 -1
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/edit_file.py +24 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/glob.py +56 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/grep.py +127 -4
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/list_files.py +24 -5
- iac_code-0.4.1/src/iac_code/tools/path_safety.py +218 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/read_file.py +99 -19
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/tool_executor.py +1 -9
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/web_fetch.py +54 -6
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/write_file.py +24 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/types/permissions.py +1 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/types/stream_events.py +9 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/banner.py +15 -2
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/core/input_history.py +5 -5
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/core/prompt_input.py +10 -8
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/dialogs/resume_picker.py +31 -8
- iac_code-0.4.1/src/iac_code/ui/dialogs/skills_picker.py +297 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/renderer.py +246 -46
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/repl.py +378 -101
- iac_code-0.4.1/src/iac_code/ui/suggestions/command_provider.py +135 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/suggestions/shell_history_provider.py +36 -2
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/suggestions/token_extractor.py +20 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/utils/file_security.py +19 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/utils/project_paths.py +44 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code.egg-info/PKG-INFO +1 -1
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code.egg-info/SOURCES.txt +16 -0
- iac_code-0.3.1/src/iac_code/__init__.py +0 -2
- iac_code-0.3.1/src/iac_code/i18n/locales/de/LC_MESSAGES/messages.mo +0 -0
- iac_code-0.3.1/src/iac_code/i18n/locales/es/LC_MESSAGES/messages.mo +0 -0
- iac_code-0.3.1/src/iac_code/i18n/locales/fr/LC_MESSAGES/messages.mo +0 -0
- iac_code-0.3.1/src/iac_code/i18n/locales/ja/LC_MESSAGES/messages.mo +0 -0
- iac_code-0.3.1/src/iac_code/i18n/locales/pt/LC_MESSAGES/messages.mo +0 -0
- iac_code-0.3.1/src/iac_code/i18n/locales/zh/LC_MESSAGES/messages.mo +0 -0
- iac_code-0.3.1/src/iac_code/services/token_counter.py +0 -81
- iac_code-0.3.1/src/iac_code/tools/bash/path_validation.py +0 -118
- iac_code-0.3.1/src/iac_code/ui/suggestions/command_provider.py +0 -43
- {iac_code-0.3.1 → iac_code-0.4.1}/LICENSE +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/MANIFEST.in +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/README.md +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/pyproject.toml +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/setup.cfg +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/setup.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/agent_card.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/app.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/artifacts.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/client.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/events.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/exposure.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/metrics.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/parts.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/persistence.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/push.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/push_queue.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/push_secrets.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/push_worker.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/router.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/signing.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/task_store.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/transport.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/transports/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/transports/base.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/transports/dispatcher.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/transports/grpc.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/transports/grpc_jsonrpc.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/transports/http.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/transports/redis_streams.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/transports/stdio.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/transports/unix.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/transports/websocket.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/a2a/types.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/acp/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/acp/convert.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/acp/http_sse.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/acp/mcp.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/acp/metrics.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/acp/state.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/acp/types.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/acp/version.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/agent/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/agent/message.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/agent/system_prompt.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/cli/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/cli/headless.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/cli/install_git_bash.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/cli/output_formats.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/commands/compact.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/commands/debug.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/commands/effort.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/commands/exit.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/commands/help.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/memory/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/providers/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/providers/anthropic_provider.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/providers/azure_openai_provider.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/providers/base.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/providers/dashscope_provider.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/providers/deepseek_provider.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/providers/gemini_provider.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/providers/kimi_provider.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/providers/lmstudio_provider.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/providers/minimax_provider.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/providers/modelscope_provider.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/providers/ollama_provider.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/providers/retry.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/providers/siliconflow_provider.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/providers/stream_watchdog.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/providers/volcengine_provider.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/providers/zhipu_provider.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/capabilities/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/capabilities/auto_detect.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/capabilities/multimodal.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/cloud_credentials.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/permissions/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/permissions/storage.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/providers/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/qwenpaw_source.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/telemetry/attributes.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/telemetry/client.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/telemetry/config.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/telemetry/constants.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/telemetry/content_serializer.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/telemetry/events.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/telemetry/fallback.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/telemetry/metrics.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/telemetry/names.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/telemetry/sanitize.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/telemetry/sink.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/telemetry/tracing.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/telemetry/types.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/services/token_budget.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/skills/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/skills/auto_trigger.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/skills/bundled/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/auto_trigger.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/ecs.md +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/oss.md +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/rds.md +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/redis.md +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/slb.md +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/references/cloud-products/vpc.md +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/references/ros-template.md +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/references/template-parameters.md +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/references/terraform-template.md +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/skills/bundled/iac_aliyun/scripts/tf2ros.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/skills/bundled/simplify.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/skills/discovery.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/skills/frontmatter.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/skills/listing.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/skills/loader.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/skills/processor.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/skills/skill_definition.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/state/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/state/app_state.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tasks/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tasks/notification_queue.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/bash/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/bash/bash_tool.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/bash/command_parser.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/bash/mode_validation.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/bash/rule_matching.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/cloud/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/aliyun_doc_search.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/api_hooks.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/endpoints.yml +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/hooks/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/hooks/ros_parameters.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/hooks/ros_validate.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/ros_yaml.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/cloud/aliyun/user_agent.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/cloud/base_api.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/tools/result_storage.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/types/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/types/skill_source.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/components/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/components/dialog.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/components/divider.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/components/fuzzy_picker.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/components/progress_bar.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/components/search_box.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/components/select.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/components/status_icon.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/components/tabs.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/core/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/core/in_place_render.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/core/key_event.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/core/raw_input.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/core/raw_input_win.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/core/screen.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/dialogs/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/dialogs/global_search.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/dialogs/history_search.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/dialogs/model_picker.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/dialogs/quick_open.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/keybindings/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/keybindings/manager.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/spinner.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/suggestions/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/suggestions/aggregator.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/suggestions/directory_provider.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/suggestions/file_provider.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/suggestions/skill_provider.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/suggestions/types.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/ui/transcript_view.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/utils/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/utils/background_housekeeping.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/utils/cleanup.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/utils/console.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/utils/image/__init__.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/utils/image/clipboard.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/utils/image/format_detect.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/utils/image/pasted_content.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/utils/image/processor.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/utils/image/resizer.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/utils/image/store.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/utils/json_utils.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/utils/log.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/utils/platform.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/utils/signals.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/utils/tool_input_parser.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code/utils/windows_paths.py +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code.egg-info/dependency_links.txt +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code.egg-info/entry_points.txt +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code.egg-info/requires.txt +0 -0
- {iac_code-0.3.1 → iac_code-0.4.1}/src/iac_code.egg-info/top_level.txt +0 -0
|
@@ -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
|
-
|
|
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):
|
|
@@ -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,
|
|
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
|
-
|
|
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=
|
|
245
|
+
session_id=entry.session_id,
|
|
246
|
+
cwd=entry.cwd or cwd or "",
|
|
247
|
+
title=entry.title,
|
|
248
248
|
)
|
|
249
|
-
for
|
|
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(
|
|
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,
|
|
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,
|
|
388
|
-
|
|
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
|
-
|
|
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,
|
|
398
|
-
raise
|
|
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,
|
|
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=
|
|
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(
|
|
422
|
-
|
|
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(
|
|
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
|
# ---------------------------------------------------------------------------
|
|
@@ -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,
|
|
@@ -165,9 +179,11 @@ class ACPSession:
|
|
|
165
179
|
conn: acp.Client,
|
|
166
180
|
mcp_configs: list[dict] | None = None,
|
|
167
181
|
metrics: ACPMetrics | None = None,
|
|
182
|
+
memory_manager=None,
|
|
168
183
|
) -> None:
|
|
169
184
|
self.id = session_id
|
|
170
185
|
self.agent_loop = agent_loop
|
|
186
|
+
self.memory_manager = memory_manager
|
|
171
187
|
self._conn = conn
|
|
172
188
|
self._current_task: asyncio.Task | None = None
|
|
173
189
|
self._replay_task: asyncio.Task[None] | None = None
|
|
@@ -292,7 +308,11 @@ class ACPSession:
|
|
|
292
308
|
prompt_text = acp_blocks_to_prompt_text(prompt)
|
|
293
309
|
slash_registry = ACPSlashRegistry()
|
|
294
310
|
if slash_registry.is_slash_command(prompt_text):
|
|
295
|
-
result = await slash_registry.execute(
|
|
311
|
+
result = await slash_registry.execute(
|
|
312
|
+
prompt_text,
|
|
313
|
+
self.agent_loop,
|
|
314
|
+
memory_manager=self.memory_manager,
|
|
315
|
+
)
|
|
296
316
|
await self._conn.session_update(
|
|
297
317
|
session_id=self.id,
|
|
298
318
|
update=acp.schema.AgentMessageChunk(
|
|
@@ -451,7 +471,7 @@ class ACPSession:
|
|
|
451
471
|
kind="allow_always",
|
|
452
472
|
)
|
|
453
473
|
)
|
|
454
|
-
|
|
474
|
+
elif _tool_supports_blanket_allow(self.agent_loop, tool_name):
|
|
455
475
|
options.append(
|
|
456
476
|
acp.schema.PermissionOption(
|
|
457
477
|
option_id=_OPTION_ALLOW_ALWAYS,
|
|
@@ -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 /
|
|
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
|
|
@@ -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)
|
|
@@ -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(
|
|
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(
|
|
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(
|
|
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:
|