sgar 0.1.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- core/__init__.py +7 -0
- core/cc/__init__.py +119 -0
- core/cc/agents/__init__.py +10 -0
- core/cc/agents/agent_tool.py +320 -0
- core/cc/agents/backends/__init__.py +11 -0
- core/cc/agents/backends/base.py +62 -0
- core/cc/agents/backends/in_process.py +126 -0
- core/cc/agents/backends/local_subprocess.py +332 -0
- core/cc/agents/definitions.py +25 -0
- core/cc/agents/runtime.py +208 -0
- core/cc/agents/runtime_registry.py +88 -0
- core/cc/agents/runtime_transport.py +66 -0
- core/cc/agents/swarm/__init__.py +3 -0
- core/cc/agents/swarm/mailbox.py +310 -0
- core/cc/agents/task_manager.py +101 -0
- core/cc/agents/task_model.py +106 -0
- core/cc/agents/task_state_store.py +149 -0
- core/cc/api.py +413 -0
- core/cc/artifact_state.py +156 -0
- core/cc/audit.py +255 -0
- core/cc/command_runner.py +331 -0
- core/cc/config.py +830 -0
- core/cc/conversation/__init__.py +47 -0
- core/cc/conversation/agent_mode_strategy.py +129 -0
- core/cc/conversation/compact.py +69 -0
- core/cc/conversation/context_assembler.py +142 -0
- core/cc/conversation/llm_adapter.py +410 -0
- core/cc/conversation/message_store.py +98 -0
- core/cc/conversation/middleware.py +288 -0
- core/cc/conversation/mode_strategy.py +280 -0
- core/cc/conversation/models.py +73 -0
- core/cc/conversation/prompt_builder.py +162 -0
- core/cc/conversation/prompt_catalog.py +98 -0
- core/cc/conversation/protocol.py +28 -0
- core/cc/conversation/query_engine.py +483 -0
- core/cc/conversation/query_loop.py +1173 -0
- core/cc/conversation/query_loop_followup.py +105 -0
- core/cc/conversation/query_loop_implementation_sync.py +95 -0
- core/cc/conversation/query_loop_mode_transitions.py +127 -0
- core/cc/conversation/query_loop_tool_events.py +94 -0
- core/cc/conversation/session.py +119 -0
- core/cc/conversation/session_journal.py +70 -0
- core/cc/conversation/strategy_common.py +93 -0
- core/cc/conversation/tool_ledger.py +179 -0
- core/cc/conversation/turn_pipeline.py +315 -0
- core/cc/editing/__init__.py +16 -0
- core/cc/editing/facade.py +288 -0
- core/cc/editing/file_state.py +61 -0
- core/cc/editing/requests.py +76 -0
- core/cc/editing/rollback.py +150 -0
- core/cc/editing/validator.py +107 -0
- core/cc/engine_factory.py +116 -0
- core/cc/errors.py +46 -0
- core/cc/jsonl.py +105 -0
- core/cc/llm.py +112 -0
- core/cc/memory/__init__.py +26 -0
- core/cc/memory/base.py +40 -0
- core/cc/memory/extractor.py +257 -0
- core/cc/memory/models.py +133 -0
- core/cc/memory/noop_provider.py +51 -0
- core/cc/memory/policy.py +70 -0
- core/cc/memory/registry.py +30 -0
- core/cc/memory/runtime.py +289 -0
- core/cc/memory/serializer.py +53 -0
- core/cc/observability.py +54 -0
- core/cc/plan.py +99 -0
- core/cc/prompt_cc_review.md +100 -0
- core/cc/prompts/agents/worker.en.md +3 -0
- core/cc/prompts/agents/worker.zh.md +3 -0
- core/cc/prompts/system/agent_mode.en.md +34 -0
- core/cc/prompts/system/agent_mode.zh.md +34 -0
- core/cc/prompts/system/agent_mode_followup.en.md +8 -0
- core/cc/prompts/system/agent_mode_followup.zh.md +8 -0
- core/cc/prompts/system/ask_mode.en.md +30 -0
- core/cc/prompts/system/ask_mode.zh.md +30 -0
- core/cc/prompts/system/code_build.en.md +36 -0
- core/cc/prompts/system/code_build.zh.md +36 -0
- core/cc/prompts/system/coordinator.en.md +3 -0
- core/cc/prompts/system/coordinator.zh.md +3 -0
- core/cc/prompts/system/default.en.md +36 -0
- core/cc/prompts/system/default.zh.md +36 -0
- core/cc/prompts/system/doc_mode.en.md +37 -0
- core/cc/prompts/system/doc_mode.zh.md +37 -0
- core/cc/prompts/system/implementation_followup.en.md +6 -0
- core/cc/prompts/system/implementation_followup.zh.md +6 -0
- core/cc/prompts/system/implementation_task_sync_followup.en.md +8 -0
- core/cc/prompts/system/implementation_task_sync_followup.zh.md +8 -0
- core/cc/prompts/system/plan_implementation.en.md +32 -0
- core/cc/prompts/system/plan_implementation.zh.md +32 -0
- core/cc/prompts/system/plan_mode.en.md +52 -0
- core/cc/prompts/system/plan_mode.zh.md +52 -0
- core/cc/prompts/system/query_continue.en.md +1 -0
- core/cc/prompts/system/query_continue.zh.md +1 -0
- core/cc/prompts/system/render_followup.en.md +5 -0
- core/cc/prompts/system/render_followup.zh.md +5 -0
- core/cc/prompts/system/spec_mode.en.md +41 -0
- core/cc/prompts/system/spec_mode.zh.md +41 -0
- core/cc/prompts/system/spec_render.en.md +35 -0
- core/cc/prompts/system/spec_render.zh.md +35 -0
- core/cc/prompts/system/swarm_planner.en.md +35 -0
- core/cc/prompts/system/swarm_planner.zh.md +35 -0
- core/cc/prompts/system/tool_followup.en.md +1 -0
- core/cc/prompts/system/tool_followup.zh.md +1 -0
- core/cc/prompts/tools/file_edit.en.md +3 -0
- core/cc/prompts/tools/file_edit.zh.md +3 -0
- core/cc/providers.py +215 -0
- core/cc/runtime.py +29 -0
- core/cc/safety/__init__.py +11 -0
- core/cc/safety/classifier.py +82 -0
- core/cc/safety/command_rules.py +170 -0
- core/cc/safety/decision.py +17 -0
- core/cc/safety/file_rules.py +32 -0
- core/cc/safety/permission_mode.py +35 -0
- core/cc/specs.py +99 -0
- core/cc/structured_flow.py +548 -0
- core/cc/tools/__init__.py +59 -0
- core/cc/tools/base.py +94 -0
- core/cc/tools/builtin.py +80 -0
- core/cc/tools/context.py +73 -0
- core/cc/tools/enter_plan_mode.py +68 -0
- core/cc/tools/enter_spec_mode.py +68 -0
- core/cc/tools/executor.py +107 -0
- core/cc/tools/exit_plan_mode.py +95 -0
- core/cc/tools/exit_spec_mode.py +95 -0
- core/cc/tools/file_edit.py +124 -0
- core/cc/tools/file_read.py +139 -0
- core/cc/tools/file_write.py +105 -0
- core/cc/tools/glob_tool.py +79 -0
- core/cc/tools/grep_tool.py +453 -0
- core/cc/tools/memory.py +145 -0
- core/cc/tools/memory_fact.py +114 -0
- core/cc/tools/memory_search.py +62 -0
- core/cc/tools/memory_status.py +49 -0
- core/cc/tools/memory_store.py +72 -0
- core/cc/tools/orchestrator.py +249 -0
- core/cc/tools/plan_artifact_write.py +210 -0
- core/cc/tools/powershell.py +65 -0
- core/cc/tools/registry.py +29 -0
- core/cc/tools/result_mapper.py +47 -0
- core/cc/tools/run_tests.py +118 -0
- core/cc/tools/send_message.py +87 -0
- core/cc/tools/shell.py +113 -0
- core/cc/tools/spec_artifact_write.py +159 -0
- core/cc/tools/task_stop.py +77 -0
- core/cc/tools/todo_write.py +84 -0
- core/cc/tools/worktree.py +152 -0
- core/cc/usage.md +370 -0
- core/ccx/__init__.py +46 -0
- core/ccx/agents/__init__.py +17 -0
- core/ccx/agents/cc_agent.py +1149 -0
- core/ccx/agents/ccx_research_tool.py +304 -0
- core/ccx/agents/ccx_sgar_tool.py +278 -0
- core/ccx/agents/ccx_spawn_tool.py +357 -0
- core/ccx/agents/ccx_tool.py +571 -0
- core/ccx/agents/ctx_search_tool.py +372 -0
- core/ccx/agents/event_bridge.py +305 -0
- core/ccx/agents/goal_prompts.py +260 -0
- core/ccx/agents/governed_goal.py +1388 -0
- core/ccx/agents/governed_run.py +444 -0
- core/ccx/agents/governed_spawn.py +470 -0
- core/ccx/agents/metadata_inheritance.py +143 -0
- core/ccx/agents/read_only_runner.py +178 -0
- core/ccx/agents/research_runner.py +340 -0
- core/ccx/agents/subagent.py +216 -0
- core/ccx/agents/swarm/__init__.py +18 -0
- core/ccx/agents/swarm/coordinator.py +379 -0
- core/ccx/agents/swarm/mailbox_bridge.py +155 -0
- core/ccx/agents/swarm/team_runtime.py +365 -0
- core/ccx/agents/task_manager.py +402 -0
- core/ccx/api.py +2346 -0
- core/ccx/audit/__init__.py +95 -0
- core/ccx/audit/check_template.py +61 -0
- core/ccx/audit/code_task.py +320 -0
- core/ccx/audit/contract.py +95 -0
- core/ccx/audit/finding_ledger.py +143 -0
- core/ccx/audit/gitdiff.py +208 -0
- core/ccx/audit/ledger.py +92 -0
- core/ccx/audit/mutation.py +258 -0
- core/ccx/audit/regression_capture.py +276 -0
- core/ccx/audit/wiring.py +119 -0
- core/ccx/conversation/__init__.py +15 -0
- core/ccx/llm_monitor.py +1002 -0
- core/ccx/memory/__init__.py +36 -0
- core/ccx/memory/inject.py +32 -0
- core/ccx/memory/models.py +292 -0
- core/ccx/memory/recall.py +146 -0
- core/ccx/memory/store.py +272 -0
- core/ccx/memory/summarizer.py +174 -0
- core/ccx/modes/__init__.py +50 -0
- core/ccx/modes/_goal.py +19 -0
- core/ccx/modes/_paths.py +14 -0
- core/ccx/modes/_sgar_command_helpers.py +611 -0
- core/ccx/modes/_text_masking.py +83 -0
- core/ccx/modes/agent.py +245 -0
- core/ccx/modes/artifacts.py +189 -0
- core/ccx/modes/ask.py +256 -0
- core/ccx/modes/blueprint.py +82 -0
- core/ccx/modes/diagnostics.py +259 -0
- core/ccx/modes/doc.py +4825 -0
- core/ccx/modes/llm_client.py +223 -0
- core/ccx/modes/parsing.py +216 -0
- core/ccx/modes/plan.py +550 -0
- core/ccx/modes/prompts/__init__.py +234 -0
- core/ccx/modes/prompts/agent.toml +13 -0
- core/ccx/modes/prompts/doc_decompose.toml +71 -0
- core/ccx/modes/prompts/doc_investigator.toml +293 -0
- core/ccx/modes/prompts/doc_prose_to_json.toml +85 -0
- core/ccx/modes/prompts/doc_surveyor.toml +103 -0
- core/ccx/modes/prompts/plan.toml +40 -0
- core/ccx/modes/prompts/spec.toml +28 -0
- core/ccx/modes/prompts/watch_analyzer.toml +179 -0
- core/ccx/modes/prompts/watch_fixer.toml +74 -0
- core/ccx/modes/sgarx.py +83 -0
- core/ccx/modes/spec.py +375 -0
- core/ccx/modes/watch.py +2220 -0
- core/ccx/modes/watch_checks.py +786 -0
- core/ccx/mypy.ini +32 -0
- core/ccx/prompt_ccx_self_review.md +103 -0
- core/ccx/prompts.py +42 -0
- core/ccx/report.py +950 -0
- core/ccx/runtime.py +1015 -0
- core/ccx/services/__init__.py +27 -0
- core/ccx/services/cost_events.py +60 -0
- core/ccx/services/findings_collector.py +83 -0
- core/ccx/services/governance_events.py +132 -0
- core/ccx/services/repository_outline.py +152 -0
- core/ccx/services/steer_inbox.py +140 -0
- core/ccx/sgar/__init__.py +53 -0
- core/ccx/sgar/__main__.py +5 -0
- core/ccx/sgar/autobuild.py +189 -0
- core/ccx/sgar/checks.py +302 -0
- core/ccx/sgar/cli.py +325 -0
- core/ccx/sgar/missions.py +474 -0
- core/ccx/sgar/models.py +208 -0
- core/ccx/sgar/runtime.py +906 -0
- core/ccx/sgar/store.py +381 -0
- core/ccx/sgar/tracing.py +212 -0
- core/ccx/sgar/validation.py +247 -0
- core/ccx/sgarx/__init__.py +13 -0
- core/ccx/sgarx/runtime.py +661 -0
- core/ccx/sgarx/store.py +26 -0
- core/ccx/structured_flow.py +346 -0
- core/ccx/templates/report.html.j2 +547 -0
- core/ccx/watch.py +1326 -0
- core/deepstack_v5/__init__.py +66 -0
- core/deepstack_v5/config.py +97 -0
- core/deepstack_v5/control/__init__.py +0 -0
- core/deepstack_v5/control/budget.py +141 -0
- core/deepstack_v5/control/controller.py +159 -0
- core/deepstack_v5/control/escalation.py +109 -0
- core/deepstack_v5/engine.py +1530 -0
- core/deepstack_v5/events.py +164 -0
- core/deepstack_v5/execution/__init__.py +0 -0
- core/deepstack_v5/execution/assignment.py +121 -0
- core/deepstack_v5/execution/dispatch_context.py +94 -0
- core/deepstack_v5/execution/dispatcher.py +764 -0
- core/deepstack_v5/execution/graph.py +346 -0
- core/deepstack_v5/execution/node.py +214 -0
- core/deepstack_v5/execution/toolcall.py +152 -0
- core/deepstack_v5/knowledge/__init__.py +0 -0
- core/deepstack_v5/knowledge/claims.py +254 -0
- core/deepstack_v5/knowledge/compaction.py +209 -0
- core/deepstack_v5/memory/__init__.py +48 -0
- core/deepstack_v5/memory/content_store.py +816 -0
- core/deepstack_v5/memory/priority.py +122 -0
- core/deepstack_v5/memory/resume.py +128 -0
- core/deepstack_v5/memory/snapshot.py +262 -0
- core/deepstack_v5/persistence/__init__.py +31 -0
- core/deepstack_v5/persistence/db.py +436 -0
- core/deepstack_v5/persistence/file_backend.py +512 -0
- core/deepstack_v5/persistence/outbox.py +98 -0
- core/deepstack_v5/persistence/stores.py +1094 -0
- core/deepstack_v5/runtime.py +210 -0
- core/deepstack_v5/types.py +454 -0
- core/llms/__init__.py +1 -0
- core/llms/_deepseek_stream.py +400 -0
- core/llms/_llm_api_client.py +600 -0
- core/llms/ali_deep_seek_client.py +16 -0
- core/llms/ali_deep_seek_r1_client.py +16 -0
- core/llms/ark_client.py +16 -0
- core/llms/aws_deepseek_client.py +18 -0
- core/llms/aws_deepseek_r1_client.py +19 -0
- core/llms/azure_deep_seek_client.py +132 -0
- core/llms/baichuan_client.py +129 -0
- core/llms/bce_deep_seek_client.py +20 -0
- core/llms/bce_deep_seek_r1_client.py +20 -0
- core/llms/claude_aws_client.py +676 -0
- core/llms/claude_client.py +365 -0
- core/llms/deepbricks_client.py +16 -0
- core/llms/doubao_client.py +238 -0
- core/llms/dummy_client.py +32 -0
- core/llms/ernie_client.py +277 -0
- core/llms/fallback_client.py +181 -0
- core/llms/gemini2_client.py +385 -0
- core/llms/gemini_client.py +596 -0
- core/llms/gemini_pro_client.py +18 -0
- core/llms/glm_client.py +469 -0
- core/llms/glm_free_client.py +14 -0
- core/llms/glm_openai_client.py +19 -0
- core/llms/healer_alpha_client.py +16 -0
- core/llms/hunter_alpha_client.py +16 -0
- core/llms/hunyuan_client.py +266 -0
- core/llms/infini_deep_seek_client.py +23 -0
- core/llms/infini_deep_seek_r1_client.py +19 -0
- core/llms/kimi_client.py +21 -0
- core/llms/llm_factory.py +98 -0
- core/llms/mi_client.py +21 -0
- core/llms/mimo_client.py +16 -0
- core/llms/mini_max_client.py +308 -0
- core/llms/mini_max_pro.py +174 -0
- core/llms/mini_max_text_client.py +11 -0
- core/llms/minimax_r1_client.py +19 -0
- core/llms/minmax_m2_client.py +21 -0
- core/llms/moonshot_client.py +359 -0
- core/llms/multi_claude.py +456 -0
- core/llms/ollama_ds32b_client.py +21 -0
- core/llms/open_router_client.py +16 -0
- core/llms/openai_client.py +555 -0
- core/llms/openai_http_client.py +806 -0
- core/llms/ppio_claude_client.py +19 -0
- core/llms/ppio_claude_sonnet_client.py +19 -0
- core/llms/ppio_client.py +18 -0
- core/llms/ppio_cluade_opus_client.py +19 -0
- core/llms/ppio_deep_seek_client.py +18 -0
- core/llms/ppio_deep_seek_r1_cleint.py +19 -0
- core/llms/ppio_gemini_pro_client.py +19 -0
- core/llms/ppio_openai_client.py +19 -0
- core/llms/ppio_r_client.py +19 -0
- core/llms/px_client.py +442 -0
- core/llms/qianwen14_client.py +15 -0
- core/llms/qianwen32_client.py +15 -0
- core/llms/qianwen_client.py +442 -0
- core/llms/qianwen_coder_client.py +16 -0
- core/llms/qianwen_coder_plus_client.py +15 -0
- core/llms/qianwen_coder_turbo_client.py +15 -0
- core/llms/qianwen_plus.py +17 -0
- core/llms/qianwen_qwq_client.py +15 -0
- core/llms/qianwen_turbo.py +26 -0
- core/llms/qiniu_deep_seek_client.py +27 -0
- core/llms/qiniu_deep_seek_r1_client.py +23 -0
- core/llms/qwq_client.py +18 -0
- core/llms/sense_deep_seek_client.py +20 -0
- core/llms/sense_deep_seek_r1_client.py +24 -0
- core/llms/silicon_deep_seek_client.py +18 -0
- core/llms/silicon_deep_seek_r1_client.py +19 -0
- core/llms/simple_azure.py +336 -0
- core/llms/simple_claude.py +389 -0
- core/llms/simple_deep_seek_client.py +846 -0
- core/llms/simple_deep_seek_client_chat.py +116 -0
- core/llms/simple_deep_seek_client_reasoning.py +88 -0
- core/llms/simple_deep_seek_client_speciale.py +20 -0
- core/llms/simple_doubao_client.py +241 -0
- core/llms/spark_client.py +16 -0
- core/llms/tencent_deep_seek_client.py +21 -0
- core/llms/tencent_deep_seek_r1_client.py +23 -0
- core/llms/volc_deep_seek_client.py +19 -0
- core/llms/volc_deep_seek_r1_client.py +19 -0
- core/llms/zero1_improver_client.py +17 -0
- core/utils/CODE_MODIFICATION_TOOLS.md +628 -0
- core/utils/CODE_UPDATE_QUICKSTART.md +409 -0
- core/utils/FEE_READER_USAGE.md +270 -0
- core/utils/FIX_LLM_CLIENT_CHAT_METHOD.md +257 -0
- core/utils/UNIFIED_LOGGER_README.md +278 -0
- core/utils/__init__.py +43 -0
- core/utils/_legacy/llm_ast_editor.md +943 -0
- core/utils/autonomous_code_agent.md +558 -0
- core/utils/code_editor.md +340 -0
- core/utils/common.py +47 -0
- core/utils/config_setting.py +52 -0
- core/utils/editor_fallback.py +84 -0
- core/utils/error_classifier_helper.py +353 -0
- core/utils/handle_max_tokens.py +63 -0
- core/utils/how_to_use_code_editor.md +792 -0
- core/utils/json_from_text.py +694 -0
- core/utils/llm_block_editor.py +3204 -0
- core/utils/llm_block_editor_lnfree.py +1096 -0
- core/utils/llm_code_editor.py +1447 -0
- core/utils/log.py +250 -0
- core/utils/prompt_language.py +81 -0
- core/utils/rate_limit.py +35 -0
- core/utils/retry.py +19 -0
- core/utils/robust_llm_editor.py +1009 -0
- core/utils/single_ton.py +25 -0
- core/utils/smart_llm_editor_v2.py +1565 -0
- core/utils/source_code_manager.py +363 -0
- sgar/__init__.py +3 -0
- sgar/__main__.py +5 -0
- sgar-0.1.3.dist-info/METADATA +90 -0
- sgar-0.1.3.dist-info/RECORD +393 -0
- sgar-0.1.3.dist-info/WHEEL +5 -0
- sgar-0.1.3.dist-info/entry_points.txt +2 -0
- sgar-0.1.3.dist-info/licenses/LICENSE +6 -0
- sgar-0.1.3.dist-info/top_level.txt +2 -0
core/__init__.py
ADDED
core/cc/__init__.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# ---------------------------------------------------------------------------
|
|
2
|
+
# Public API (stable, recommended for external integrations)
|
|
3
|
+
# ---------------------------------------------------------------------------
|
|
4
|
+
from .audit import (
|
|
5
|
+
RuntimeAuditQuery,
|
|
6
|
+
RuntimeAuditSnapshot,
|
|
7
|
+
RuntimeAuditSummary,
|
|
8
|
+
query_runtime_audit,
|
|
9
|
+
read_runtime_audit,
|
|
10
|
+
summarize_runtime_audit,
|
|
11
|
+
)
|
|
12
|
+
from .api import (
|
|
13
|
+
AgentRunRequest,
|
|
14
|
+
AgentRunResult,
|
|
15
|
+
CodeAgent,
|
|
16
|
+
CodeBuildRequest,
|
|
17
|
+
build_code_with_agent,
|
|
18
|
+
run_code_agent,
|
|
19
|
+
)
|
|
20
|
+
from .config import CCConfig, load_cc_config
|
|
21
|
+
|
|
22
|
+
# ---------------------------------------------------------------------------
|
|
23
|
+
# Advanced API (stable but lower-level; for fine-grained session control)
|
|
24
|
+
# ---------------------------------------------------------------------------
|
|
25
|
+
from .llm import DefaultLLMClientProvider, LLMClientProvider
|
|
26
|
+
from .runtime import build_default_query_engine
|
|
27
|
+
from .conversation.query_engine import QueryEngine
|
|
28
|
+
from .conversation.session import QuerySession, SessionFactory
|
|
29
|
+
from .conversation.models import SessionEvent, SessionMessage
|
|
30
|
+
|
|
31
|
+
# ---------------------------------------------------------------------------
|
|
32
|
+
# Middleware pipeline (composable cross-cutting concerns)
|
|
33
|
+
# ---------------------------------------------------------------------------
|
|
34
|
+
from .conversation.middleware import (
|
|
35
|
+
RetryPolicy,
|
|
36
|
+
TurnHooks,
|
|
37
|
+
TurnMiddleware,
|
|
38
|
+
TurnRunner,
|
|
39
|
+
apply as apply_middleware,
|
|
40
|
+
pipe as pipe_middleware,
|
|
41
|
+
with_compaction,
|
|
42
|
+
with_hooks,
|
|
43
|
+
with_persistence,
|
|
44
|
+
with_retry,
|
|
45
|
+
with_turn_tracking,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# ---------------------------------------------------------------------------
|
|
49
|
+
# Environment providers (injectable filesystem / shell abstractions)
|
|
50
|
+
# ---------------------------------------------------------------------------
|
|
51
|
+
from .providers import (
|
|
52
|
+
CommandProvider,
|
|
53
|
+
Environment,
|
|
54
|
+
FileSystemProvider,
|
|
55
|
+
LocalCommandProvider,
|
|
56
|
+
LocalFileSystemProvider,
|
|
57
|
+
default_environment,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# ---------------------------------------------------------------------------
|
|
61
|
+
# Errors
|
|
62
|
+
# ---------------------------------------------------------------------------
|
|
63
|
+
from .errors import (
|
|
64
|
+
CCError,
|
|
65
|
+
ConfigError,
|
|
66
|
+
ToolExecutionError,
|
|
67
|
+
ToolValidationError,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
__all__ = [
|
|
71
|
+
# -- Public API --
|
|
72
|
+
"AgentRunRequest",
|
|
73
|
+
"AgentRunResult",
|
|
74
|
+
"CCConfig",
|
|
75
|
+
"CodeAgent",
|
|
76
|
+
"CodeBuildRequest",
|
|
77
|
+
"RuntimeAuditQuery",
|
|
78
|
+
"RuntimeAuditSnapshot",
|
|
79
|
+
"RuntimeAuditSummary",
|
|
80
|
+
"build_code_with_agent",
|
|
81
|
+
"load_cc_config",
|
|
82
|
+
"query_runtime_audit",
|
|
83
|
+
"read_runtime_audit",
|
|
84
|
+
"run_code_agent",
|
|
85
|
+
"summarize_runtime_audit",
|
|
86
|
+
# -- Advanced API --
|
|
87
|
+
"DefaultLLMClientProvider",
|
|
88
|
+
"LLMClientProvider",
|
|
89
|
+
"QueryEngine",
|
|
90
|
+
"QuerySession",
|
|
91
|
+
"SessionEvent",
|
|
92
|
+
"SessionFactory",
|
|
93
|
+
"SessionMessage",
|
|
94
|
+
"build_default_query_engine",
|
|
95
|
+
# -- Middleware pipeline --
|
|
96
|
+
"RetryPolicy",
|
|
97
|
+
"TurnHooks",
|
|
98
|
+
"TurnMiddleware",
|
|
99
|
+
"TurnRunner",
|
|
100
|
+
"apply_middleware",
|
|
101
|
+
"pipe_middleware",
|
|
102
|
+
"with_compaction",
|
|
103
|
+
"with_hooks",
|
|
104
|
+
"with_persistence",
|
|
105
|
+
"with_retry",
|
|
106
|
+
"with_turn_tracking",
|
|
107
|
+
# -- Environment providers --
|
|
108
|
+
"CommandProvider",
|
|
109
|
+
"Environment",
|
|
110
|
+
"FileSystemProvider",
|
|
111
|
+
"LocalCommandProvider",
|
|
112
|
+
"LocalFileSystemProvider",
|
|
113
|
+
"default_environment",
|
|
114
|
+
# -- Errors --
|
|
115
|
+
"CCError",
|
|
116
|
+
"ConfigError",
|
|
117
|
+
"ToolExecutionError",
|
|
118
|
+
"ToolValidationError",
|
|
119
|
+
]
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
import logging
|
|
5
|
+
import os
|
|
6
|
+
from typing import Any
|
|
7
|
+
import asyncio
|
|
8
|
+
|
|
9
|
+
from ..agents.backends import InProcessBackend, LocalSubprocessBackend, RuntimeBackend
|
|
10
|
+
from ..config import CCConfig
|
|
11
|
+
from ..conversation.session import QuerySession, SessionFactory
|
|
12
|
+
from ..llm import DefaultLLMClientProvider, LLMClientProvider
|
|
13
|
+
from ..observability import EventRecord, JsonlAuditLogger
|
|
14
|
+
from ..tools.base import BaseTool, ToolCall, ToolResult, ToolSpec, ValidationResult
|
|
15
|
+
from ..tools.context import ToolUseContext
|
|
16
|
+
from .definitions import AgentDefinition
|
|
17
|
+
from .runtime import AgentRuntime
|
|
18
|
+
from .runtime_registry import InProcessRuntimeRegistry, get_in_process_runtime_registry
|
|
19
|
+
from .task_manager import TaskManager
|
|
20
|
+
from .task_model import AgentTask, AgentTaskStatus
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# Maximum recursion depth for the ``agent`` tool. With cc_query_loop as the
|
|
27
|
+
# default ccx runner kind, every agent node can call ``agent`` to spawn a
|
|
28
|
+
# child runtime, which itself can call ``agent``, and so on. Each level
|
|
29
|
+
# multiplies the cost by the per-node fan-out, so an unconstrained recursion
|
|
30
|
+
# of depth 4 with fan-out 3 is already 81 leaves. The cap here is the only
|
|
31
|
+
# mechanical (non-prompt) backstop — LLMs can't reliably stay within a
|
|
32
|
+
# nominal budget via prompt instructions, but the runtime can refuse the
|
|
33
|
+
# call.
|
|
34
|
+
#
|
|
35
|
+
# Depth 0 = the top-level cc engine driven by ccx's CcAgentRunner; depth 1
|
|
36
|
+
# = its first helper; depth 2 = a helper-of-helper. The default of 3 keeps
|
|
37
|
+
# room for a real "lead → researcher → reviewer" pattern without permitting
|
|
38
|
+
# unbounded chains. Override via the ``CC_MAX_AGENT_RECURSION_DEPTH``
|
|
39
|
+
# environment variable for unusual workloads.
|
|
40
|
+
_DEFAULT_MAX_AGENT_RECURSION_DEPTH = 3
|
|
41
|
+
_RECURSION_DEPTH_STATE_KEY = "agent_recursion_depth"
|
|
42
|
+
_AGENT_SPAWN_REFUSED_ERROR_CODE = "AT1100"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _resolve_max_recursion_depth() -> int:
|
|
46
|
+
raw = os.environ.get("CC_MAX_AGENT_RECURSION_DEPTH")
|
|
47
|
+
if not raw:
|
|
48
|
+
return _DEFAULT_MAX_AGENT_RECURSION_DEPTH
|
|
49
|
+
try:
|
|
50
|
+
value = int(raw)
|
|
51
|
+
except ValueError:
|
|
52
|
+
logger.warning(
|
|
53
|
+
"CC_MAX_AGENT_RECURSION_DEPTH=%r is not an int; using default %d",
|
|
54
|
+
raw, _DEFAULT_MAX_AGENT_RECURSION_DEPTH,
|
|
55
|
+
)
|
|
56
|
+
return _DEFAULT_MAX_AGENT_RECURSION_DEPTH
|
|
57
|
+
if value < 0:
|
|
58
|
+
logger.warning(
|
|
59
|
+
"CC_MAX_AGENT_RECURSION_DEPTH=%d is negative; using default %d",
|
|
60
|
+
value, _DEFAULT_MAX_AGENT_RECURSION_DEPTH,
|
|
61
|
+
)
|
|
62
|
+
return _DEFAULT_MAX_AGENT_RECURSION_DEPTH
|
|
63
|
+
return value
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass(slots=True)
|
|
67
|
+
class AgentToolRequest:
|
|
68
|
+
description: str
|
|
69
|
+
prompt: str
|
|
70
|
+
subagent_type: str | None = None
|
|
71
|
+
backend: str | None = None
|
|
72
|
+
model: str | None = None
|
|
73
|
+
run_in_background: bool = False
|
|
74
|
+
name: str | None = None
|
|
75
|
+
team_name: str | None = None
|
|
76
|
+
mode: str | None = None
|
|
77
|
+
isolation: str | None = None
|
|
78
|
+
cwd: str | None = None
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class AgentTool(BaseTool):
|
|
82
|
+
def __init__(
|
|
83
|
+
self,
|
|
84
|
+
llm_client_provider: LLMClientProvider | None = None,
|
|
85
|
+
runtime_registry: InProcessRuntimeRegistry | None = None,
|
|
86
|
+
) -> None:
|
|
87
|
+
super().__init__(
|
|
88
|
+
ToolSpec(
|
|
89
|
+
name="agent",
|
|
90
|
+
description="Spawn a child agent to work on a sub-task and return its result.",
|
|
91
|
+
input_schema={
|
|
92
|
+
"type": "object",
|
|
93
|
+
"properties": {
|
|
94
|
+
"description": {"type": "string"},
|
|
95
|
+
"prompt": {"type": "string"},
|
|
96
|
+
"subagent_type": {"type": "string"},
|
|
97
|
+
"backend": {"type": "string"},
|
|
98
|
+
"run_in_background": {"type": "boolean"},
|
|
99
|
+
"cwd": {"type": "string"},
|
|
100
|
+
},
|
|
101
|
+
"required": ["description", "prompt"],
|
|
102
|
+
},
|
|
103
|
+
)
|
|
104
|
+
)
|
|
105
|
+
self.llm_client_provider = llm_client_provider or DefaultLLMClientProvider()
|
|
106
|
+
self.runtime_registry = runtime_registry
|
|
107
|
+
|
|
108
|
+
def validate_input(self, arguments: dict[str, Any]) -> ValidationResult:
|
|
109
|
+
if not arguments.get("description"):
|
|
110
|
+
return ValidationResult(ok=False, message="description is required.")
|
|
111
|
+
if not arguments.get("prompt"):
|
|
112
|
+
return ValidationResult(ok=False, message="prompt is required.")
|
|
113
|
+
backend = arguments.get("backend")
|
|
114
|
+
if backend and str(backend) not in {"in_process", "local_subprocess"}:
|
|
115
|
+
return ValidationResult(ok=False, message=f"Unsupported backend: {backend}")
|
|
116
|
+
return ValidationResult(ok=True)
|
|
117
|
+
|
|
118
|
+
async def execute(self, tool_call: ToolCall, ctx: ToolUseContext) -> ToolResult:
|
|
119
|
+
request = AgentToolRequest(
|
|
120
|
+
description=str(tool_call.arguments["description"]),
|
|
121
|
+
prompt=str(tool_call.arguments["prompt"]),
|
|
122
|
+
subagent_type=tool_call.arguments.get("subagent_type"),
|
|
123
|
+
backend=tool_call.arguments.get("backend"),
|
|
124
|
+
run_in_background=bool(tool_call.arguments.get("run_in_background", False)),
|
|
125
|
+
cwd=tool_call.arguments.get("cwd"),
|
|
126
|
+
)
|
|
127
|
+
# Recursion depth check (Tier 1 backstop against runaway sub-agent
|
|
128
|
+
# spawning when cc_query_loop is the default ccx runner). Read the
|
|
129
|
+
# parent's depth from session metadata.state, refuse if we'd exceed
|
|
130
|
+
# the cap, and emit an audit event either way so deep chains are
|
|
131
|
+
# debuggable post-hoc.
|
|
132
|
+
parent_session = ctx.metadata.get("session")
|
|
133
|
+
parent_depth = 0
|
|
134
|
+
if parent_session is not None:
|
|
135
|
+
parent_depth = int(
|
|
136
|
+
(parent_session.metadata.state or {}).get(
|
|
137
|
+
_RECURSION_DEPTH_STATE_KEY, 0,
|
|
138
|
+
) or 0
|
|
139
|
+
)
|
|
140
|
+
max_depth = _resolve_max_recursion_depth()
|
|
141
|
+
runtime_root_for_audit = ctx.config.runtime_root_path(ctx.cwd)
|
|
142
|
+
agent_audit_logger = JsonlAuditLogger(
|
|
143
|
+
runtime_root_for_audit / "audit" / "agent_events.jsonl"
|
|
144
|
+
)
|
|
145
|
+
prospective_child_depth = parent_depth + 1
|
|
146
|
+
if prospective_child_depth > max_depth:
|
|
147
|
+
agent_audit_logger.append(EventRecord(
|
|
148
|
+
event_type="agent_spawn_refused_recursion_cap",
|
|
149
|
+
session_id=getattr(parent_session, "session_id", None),
|
|
150
|
+
tool_name="agent",
|
|
151
|
+
success=False,
|
|
152
|
+
error_code=_AGENT_SPAWN_REFUSED_ERROR_CODE,
|
|
153
|
+
details={
|
|
154
|
+
"parent_depth": parent_depth,
|
|
155
|
+
"prospective_child_depth": prospective_child_depth,
|
|
156
|
+
"max_depth": max_depth,
|
|
157
|
+
"description": (request.description or "")[:200],
|
|
158
|
+
"tool_use_id": tool_call.tool_use_id,
|
|
159
|
+
},
|
|
160
|
+
))
|
|
161
|
+
logger.warning(
|
|
162
|
+
"agent tool refused: parent_depth=%d would exceed max_depth=%d "
|
|
163
|
+
"(description=%r). Set CC_MAX_AGENT_RECURSION_DEPTH to raise the cap.",
|
|
164
|
+
parent_depth, max_depth, (request.description or "")[:120],
|
|
165
|
+
)
|
|
166
|
+
return ToolResult(
|
|
167
|
+
tool_use_id=tool_call.tool_use_id,
|
|
168
|
+
tool_name=tool_call.tool_name,
|
|
169
|
+
success=False,
|
|
170
|
+
content=(
|
|
171
|
+
f"agent spawn refused: recursion depth {prospective_child_depth} "
|
|
172
|
+
f"would exceed cap {max_depth}. Complete this task with the "
|
|
173
|
+
f"context you already have, or finalize and return."
|
|
174
|
+
),
|
|
175
|
+
error_code=_AGENT_SPAWN_REFUSED_ERROR_CODE,
|
|
176
|
+
data={
|
|
177
|
+
"parent_depth": parent_depth,
|
|
178
|
+
"max_depth": max_depth,
|
|
179
|
+
"refused": True,
|
|
180
|
+
},
|
|
181
|
+
)
|
|
182
|
+
# Approaching-cap warning (one less than max). Distinct event type
|
|
183
|
+
# so dashboards can flag chains that get close without firing on
|
|
184
|
+
# every spawn.
|
|
185
|
+
if prospective_child_depth == max_depth:
|
|
186
|
+
agent_audit_logger.append(EventRecord(
|
|
187
|
+
event_type="agent_collaboration_depth_warning",
|
|
188
|
+
session_id=getattr(parent_session, "session_id", None),
|
|
189
|
+
tool_name="agent",
|
|
190
|
+
success=None,
|
|
191
|
+
details={
|
|
192
|
+
"parent_depth": parent_depth,
|
|
193
|
+
"child_depth": prospective_child_depth,
|
|
194
|
+
"max_depth": max_depth,
|
|
195
|
+
"description": (request.description or "")[:200],
|
|
196
|
+
"tool_use_id": tool_call.tool_use_id,
|
|
197
|
+
},
|
|
198
|
+
))
|
|
199
|
+
|
|
200
|
+
definition = self.resolve_agent_definition(request)
|
|
201
|
+
backend_name = self.resolve_backend_name(request, ctx)
|
|
202
|
+
backend = self.resolve_backend(backend_name)
|
|
203
|
+
runtime_root = ctx.config.runtime_root_path(ctx.cwd)
|
|
204
|
+
runtime_registry = self.runtime_registry or get_in_process_runtime_registry(runtime_root)
|
|
205
|
+
task_manager = TaskManager(runtime_root / "tasks")
|
|
206
|
+
task = task_manager.create_task(
|
|
207
|
+
AgentTask.create(
|
|
208
|
+
agent_type=definition.agent_id,
|
|
209
|
+
backend=backend_name,
|
|
210
|
+
prompt_language=ctx.prompt_language,
|
|
211
|
+
title=request.description,
|
|
212
|
+
input_payload={
|
|
213
|
+
"description": request.description,
|
|
214
|
+
"prompt": request.prompt,
|
|
215
|
+
},
|
|
216
|
+
)
|
|
217
|
+
)
|
|
218
|
+
child_session = self.build_child_session(
|
|
219
|
+
parent_session=ctx.metadata["session"],
|
|
220
|
+
task=task,
|
|
221
|
+
agent_definition=definition,
|
|
222
|
+
cwd=request.cwd or ctx.cwd,
|
|
223
|
+
)
|
|
224
|
+
# Stamp the child's depth so its own ``agent`` tool calls see a
|
|
225
|
+
# bumped value and the cap composes across levels.
|
|
226
|
+
child_session.metadata.state[_RECURSION_DEPTH_STATE_KEY] = prospective_child_depth
|
|
227
|
+
from ..runtime import build_default_query_engine
|
|
228
|
+
|
|
229
|
+
runtime = AgentRuntime(
|
|
230
|
+
definition=definition,
|
|
231
|
+
task=task,
|
|
232
|
+
query_engine=build_default_query_engine(
|
|
233
|
+
cwd=child_session.cwd,
|
|
234
|
+
config=child_session.config,
|
|
235
|
+
llm_client_provider=self.llm_client_provider,
|
|
236
|
+
session=child_session,
|
|
237
|
+
),
|
|
238
|
+
task_manager=task_manager,
|
|
239
|
+
)
|
|
240
|
+
controller = await backend.create_controller(
|
|
241
|
+
runtime=runtime,
|
|
242
|
+
run_in_background=request.run_in_background,
|
|
243
|
+
runtime_root=runtime_root,
|
|
244
|
+
)
|
|
245
|
+
runtime_registry.register(controller)
|
|
246
|
+
if request.run_in_background:
|
|
247
|
+
if backend_name == "in_process":
|
|
248
|
+
task_manager.update_task_status(task.task_id, AgentTaskStatus.RUNNING)
|
|
249
|
+
background_task = asyncio.create_task(controller.start(request.prompt))
|
|
250
|
+
runtime_registry.register_background_task(task.runtime_id, background_task)
|
|
251
|
+
launch_result = {
|
|
252
|
+
"task_id": task.task_id,
|
|
253
|
+
"runtime_id": task.runtime_id,
|
|
254
|
+
"status": task.status.value,
|
|
255
|
+
"background": True,
|
|
256
|
+
"backend": backend_name,
|
|
257
|
+
}
|
|
258
|
+
else:
|
|
259
|
+
launch_result = await controller.start(request.prompt)
|
|
260
|
+
return ToolResult(
|
|
261
|
+
tool_use_id=tool_call.tool_use_id,
|
|
262
|
+
tool_name=tool_call.tool_name,
|
|
263
|
+
success=True,
|
|
264
|
+
content="Agent launched in background mode.",
|
|
265
|
+
data=launch_result,
|
|
266
|
+
)
|
|
267
|
+
result = await controller.start(request.prompt)
|
|
268
|
+
return ToolResult(
|
|
269
|
+
tool_use_id=tool_call.tool_use_id,
|
|
270
|
+
tool_name=tool_call.tool_name,
|
|
271
|
+
success=True,
|
|
272
|
+
content=result["final_text"],
|
|
273
|
+
data=result,
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
def resolve_backend_name(self, request: AgentToolRequest, ctx: ToolUseContext) -> str:
|
|
277
|
+
return str(request.backend or request.mode or ctx.config.default_backend or "in_process")
|
|
278
|
+
|
|
279
|
+
def resolve_backend(self, backend_name: str) -> RuntimeBackend:
|
|
280
|
+
if backend_name == "in_process":
|
|
281
|
+
return InProcessBackend()
|
|
282
|
+
if backend_name == "local_subprocess":
|
|
283
|
+
return LocalSubprocessBackend()
|
|
284
|
+
raise ValueError(f"Unsupported backend: {backend_name}")
|
|
285
|
+
|
|
286
|
+
def resolve_agent_definition(self, request: AgentToolRequest) -> AgentDefinition:
|
|
287
|
+
agent_id = request.subagent_type or "worker"
|
|
288
|
+
return AgentDefinition(
|
|
289
|
+
agent_id=agent_id,
|
|
290
|
+
name=request.name or agent_id,
|
|
291
|
+
description=request.description,
|
|
292
|
+
prompt_key="agents.worker",
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
def build_child_session(
|
|
296
|
+
self,
|
|
297
|
+
*,
|
|
298
|
+
parent_session: QuerySession,
|
|
299
|
+
task: AgentTask,
|
|
300
|
+
agent_definition: AgentDefinition,
|
|
301
|
+
cwd: str,
|
|
302
|
+
) -> QuerySession:
|
|
303
|
+
child_config = CCConfig.from_mapping(
|
|
304
|
+
{
|
|
305
|
+
**parent_session.config.to_dict(),
|
|
306
|
+
"agent_mode": "",
|
|
307
|
+
}
|
|
308
|
+
)
|
|
309
|
+
child_factory = SessionFactory(child_config)
|
|
310
|
+
child_session = child_factory.create(
|
|
311
|
+
cwd=cwd,
|
|
312
|
+
model_name=parent_session.model_name,
|
|
313
|
+
agent_id=agent_definition.agent_id,
|
|
314
|
+
parent_task_id=task.task_id,
|
|
315
|
+
)
|
|
316
|
+
child_session.prompt_language = parent_session.prompt_language
|
|
317
|
+
child_session.permission_mode = parent_session.permission_mode
|
|
318
|
+
child_session.agent_mode = ""
|
|
319
|
+
child_session.metadata.state["spawned_by_agent_mode"] = parent_session.agent_mode
|
|
320
|
+
return child_session
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from .base import BackendHandle, RuntimeBackend, RuntimeController
|
|
2
|
+
from .in_process import InProcessBackend
|
|
3
|
+
from .local_subprocess import LocalSubprocessBackend
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"BackendHandle",
|
|
7
|
+
"InProcessBackend",
|
|
8
|
+
"LocalSubprocessBackend",
|
|
9
|
+
"RuntimeBackend",
|
|
10
|
+
"RuntimeController",
|
|
11
|
+
]
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any, Protocol, TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from ..runtime import AgentMessage, AgentRuntime
|
|
9
|
+
from ..task_model import AgentTask
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(slots=True)
|
|
13
|
+
class BackendHandle:
|
|
14
|
+
runtime_id: str
|
|
15
|
+
backend_name: str
|
|
16
|
+
process_id: int | None = None
|
|
17
|
+
endpoint: str | None = None
|
|
18
|
+
output_path: str | None = None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class RuntimeController(Protocol):
|
|
22
|
+
task: "AgentTask"
|
|
23
|
+
handle: BackendHandle
|
|
24
|
+
|
|
25
|
+
async def start(self, prompt: str) -> dict[str, Any]:
|
|
26
|
+
...
|
|
27
|
+
|
|
28
|
+
async def send_message(
|
|
29
|
+
self,
|
|
30
|
+
message: "AgentMessage",
|
|
31
|
+
*,
|
|
32
|
+
timeout_seconds: float | None = None,
|
|
33
|
+
) -> dict[str, Any]:
|
|
34
|
+
...
|
|
35
|
+
|
|
36
|
+
async def stop(self, reason: str) -> None:
|
|
37
|
+
...
|
|
38
|
+
|
|
39
|
+
async def collect_status(self) -> dict[str, Any]:
|
|
40
|
+
...
|
|
41
|
+
|
|
42
|
+
async def apply_shared_state(
|
|
43
|
+
self,
|
|
44
|
+
*,
|
|
45
|
+
shared_context: dict[str, Any],
|
|
46
|
+
shared_allowed_paths: list[str],
|
|
47
|
+
timeout_seconds: float | None = None,
|
|
48
|
+
) -> dict[str, Any]:
|
|
49
|
+
...
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class RuntimeBackend(Protocol):
|
|
53
|
+
name: str
|
|
54
|
+
|
|
55
|
+
async def create_controller(
|
|
56
|
+
self,
|
|
57
|
+
*,
|
|
58
|
+
runtime: "AgentRuntime",
|
|
59
|
+
run_in_background: bool,
|
|
60
|
+
runtime_root: Path,
|
|
61
|
+
) -> RuntimeController:
|
|
62
|
+
...
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
from ..swarm.mailbox import MailboxEnvelope, MailboxStore
|
|
8
|
+
from .base import BackendHandle, RuntimeBackend, RuntimeController
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class InProcessController:
|
|
12
|
+
def __init__(self, runtime, runtime_root: Path, *, keep_alive: bool) -> None:
|
|
13
|
+
self.runtime = runtime
|
|
14
|
+
self.task = runtime.task
|
|
15
|
+
self.task_manager = runtime.task_manager
|
|
16
|
+
self.keep_alive = keep_alive
|
|
17
|
+
session = runtime.query_engine.session
|
|
18
|
+
team_id = session.metadata.team_id
|
|
19
|
+
lead_runtime_id = session.metadata.state.get("team_lead_runtime_id")
|
|
20
|
+
self.team_mailbox = (
|
|
21
|
+
MailboxStore(runtime_root / "teams" / str(team_id) / "mailbox")
|
|
22
|
+
if team_id is not None and lead_runtime_id
|
|
23
|
+
else None
|
|
24
|
+
)
|
|
25
|
+
self.lead_runtime_id = str(lead_runtime_id) if lead_runtime_id else None
|
|
26
|
+
self.handle = BackendHandle(
|
|
27
|
+
runtime_id=runtime.task.runtime_id,
|
|
28
|
+
backend_name="in_process",
|
|
29
|
+
process_id=None,
|
|
30
|
+
output_path=None,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
async def start(self, prompt: str) -> dict[str, object]:
|
|
34
|
+
return await self.runtime.start(prompt, keep_alive=self.keep_alive)
|
|
35
|
+
|
|
36
|
+
async def send_message(self, message, *, timeout_seconds: float | None = None) -> dict[str, object]:
|
|
37
|
+
del timeout_seconds
|
|
38
|
+
self._publish_team_event(
|
|
39
|
+
"status_update",
|
|
40
|
+
{
|
|
41
|
+
"message_id": message.message_id,
|
|
42
|
+
"status": "running",
|
|
43
|
+
"description": message.metadata.get("description"),
|
|
44
|
+
},
|
|
45
|
+
)
|
|
46
|
+
self._publish_team_event(
|
|
47
|
+
"partial_result",
|
|
48
|
+
{
|
|
49
|
+
"message_id": message.message_id,
|
|
50
|
+
"partial_text": f"Worker {self.task.runtime_id} started task {message.message_id}",
|
|
51
|
+
"description": message.metadata.get("description"),
|
|
52
|
+
},
|
|
53
|
+
)
|
|
54
|
+
result = await self.runtime.send_message(message)
|
|
55
|
+
self._publish_team_event(
|
|
56
|
+
"final_result",
|
|
57
|
+
{
|
|
58
|
+
"message_id": message.message_id,
|
|
59
|
+
"description": message.metadata.get("description"),
|
|
60
|
+
"final_text": result.get("final_text", ""),
|
|
61
|
+
"status": result.get("status"),
|
|
62
|
+
},
|
|
63
|
+
)
|
|
64
|
+
self._publish_team_event(
|
|
65
|
+
"assignment_completed",
|
|
66
|
+
{
|
|
67
|
+
"message_id": message.message_id,
|
|
68
|
+
"description": message.metadata.get("description"),
|
|
69
|
+
"final_text": result.get("final_text", ""),
|
|
70
|
+
"status": result.get("status"),
|
|
71
|
+
},
|
|
72
|
+
)
|
|
73
|
+
return result
|
|
74
|
+
|
|
75
|
+
async def stop(self, reason: str) -> None:
|
|
76
|
+
await self.runtime.stop(reason)
|
|
77
|
+
|
|
78
|
+
async def collect_status(self) -> dict[str, object]:
|
|
79
|
+
return await self.runtime.collect_status()
|
|
80
|
+
|
|
81
|
+
async def apply_shared_state(
|
|
82
|
+
self,
|
|
83
|
+
*,
|
|
84
|
+
shared_context: dict[str, object],
|
|
85
|
+
shared_allowed_paths: list[str],
|
|
86
|
+
timeout_seconds: float | None = None,
|
|
87
|
+
) -> dict[str, object]:
|
|
88
|
+
del timeout_seconds
|
|
89
|
+
session = self.runtime.query_engine.session
|
|
90
|
+
session.metadata.state["team_shared_context"] = dict(shared_context)
|
|
91
|
+
session.metadata.state["team_shared_allowed_paths"] = list(shared_allowed_paths)
|
|
92
|
+
session.metadata.state["allowed_paths"] = list(shared_allowed_paths)
|
|
93
|
+
return {
|
|
94
|
+
"runtime_id": self.task.runtime_id,
|
|
95
|
+
"backend": self.handle.backend_name,
|
|
96
|
+
"shared_context": dict(shared_context),
|
|
97
|
+
"shared_allowed_paths": list(shared_allowed_paths),
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
def _publish_team_event(self, message_type: str, payload: dict[str, object]) -> None:
|
|
101
|
+
if self.team_mailbox is None or self.lead_runtime_id is None:
|
|
102
|
+
return
|
|
103
|
+
message_token = str(payload.get("message_id") or "evt")
|
|
104
|
+
self.team_mailbox.enqueue(
|
|
105
|
+
MailboxEnvelope(
|
|
106
|
+
envelope_id=f"env_{message_type}_{self.task.runtime_id}_{message_token}_{int(time.time() * 1000)}",
|
|
107
|
+
team_id=self.runtime.query_engine.session.metadata.team_id,
|
|
108
|
+
from_runtime_id=self.task.runtime_id,
|
|
109
|
+
to_runtime_id=self.lead_runtime_id,
|
|
110
|
+
message_type=message_type,
|
|
111
|
+
payload=dict(payload),
|
|
112
|
+
)
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class InProcessBackend(RuntimeBackend):
|
|
117
|
+
name = "in_process"
|
|
118
|
+
|
|
119
|
+
async def create_controller(
|
|
120
|
+
self,
|
|
121
|
+
*,
|
|
122
|
+
runtime,
|
|
123
|
+
run_in_background: bool,
|
|
124
|
+
runtime_root: Path,
|
|
125
|
+
) -> RuntimeController:
|
|
126
|
+
return InProcessController(runtime, runtime_root, keep_alive=run_in_background)
|