aisolate-agent 0.3.1__tar.gz → 0.3.2__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.
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/PKG-INFO +2 -2
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/README.md +1 -1
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/pyproject.toml +1 -1
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/agent.py +24 -1
- aisolate_agent-0.3.2/src/aisolate/agent/core/__init__.py +41 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/core/context.py +29 -4
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/core/conversation_log.py +5 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/core/graph.py +28 -1
- aisolate_agent-0.3.2/src/aisolate/agent/core/initial_state.py +48 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/graphs.py +10 -3
- aisolate_agent-0.3.2/src/aisolate/agent/states/__init__.py +64 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/substrate.py +8 -8
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_architecture_assumptions.py +37 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_graph.py +80 -0
- aisolate_agent-0.3.2/src/aisolate/agent/tests/test_core/test_initial_state.py +109 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_resume_semantics.py +131 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_substrate.py +38 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_tools/test_workspace_tools.py +76 -5
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tools/actions/workspace.py +15 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tools/platform/workspace_tools.py +80 -29
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate_agent.egg-info/PKG-INFO +2 -2
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate_agent.egg-info/SOURCES.txt +2 -0
- aisolate_agent-0.3.1/src/aisolate/agent/core/__init__.py +0 -18
- aisolate_agent-0.3.1/src/aisolate/agent/states/__init__.py +0 -25
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/setup.cfg +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/__init__.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/__init__.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/config/__init__.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/config/agent_settings.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/config/prompts/__init__.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/core/artifacts.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/core/budget.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/core/events.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/core/exceptions.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/core/llm_response.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/core/policy.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/core/protocols.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/core/snapshot.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/core/spine_compaction.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/core/tests/test_context_snapshots.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/core/tests/test_snapshot.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/core/views.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/core/workspace.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/logging/__init__.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/logging/config.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/logging/factory.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/logging/unified_logger.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/main.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/memory/__init__.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/memory/models.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/states/base.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/states/clarification.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/states/compaction.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/states/critic.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/states/executing.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/states/expert_synthesis.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/states/finished.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/states/hitl_pause.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/states/outputs.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/states/planning.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/states/task.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/status.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/strategies/__init__.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/strategies/llm/__init__.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/strategies/llm/base.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/strategies/llm/harmony.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/strategies/llm/oai.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/strategies/llm/qwen.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/strategies/memory/__init__.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/strategies/memory/in_memory.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/strategies/memory/tests/test_in_memory.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/strategies/memory/vector.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/telemetry/__init__.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/telemetry/decorators.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/telemetry/factory.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/telemetry/interface.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/telemetry/noop.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/telemetry/openinference.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/telemetry/opentelemetry_impl.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/telemetry/utils.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/__init__.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/conftest.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/fakes.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/__init__.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_artifact_rows.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_change_bridge_echo.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_conversation_log.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_delegated_lineage_restore.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_event_agent_name_stamping.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_execute_turn_review_pending.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_firewall_property.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_legacy_snapshot.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_live_capture.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_locale_prelude.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_policy.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_restore_review_posture.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_run_streaming_cancel.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_spine_compaction.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_state_transitions.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_status_summarizer.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_subagent_graph.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_token_accumulation.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_turn_started_events.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_turn_usage_isolation.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_uniform_state.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_views.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_workspace_bridge_attribution.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_workspace_capture.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_workspace_changed_event.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_e2e/__init__.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_e2e/conftest.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_e2e/test_delegation_file_paths.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_e2e/test_delegation_recovery.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_e2e/test_locale_helpers.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_e2e/test_size_compaction.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_e2e/test_stall_clone_96919cf1.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_e2e/test_truncation_wedge_5b90b101.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_e2e/test_workspace_lifecycle.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_integration/__init__.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_integration/helpers.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_integration/test_flagship_loop.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_integration/test_followup_roundtrips.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_integration/test_full_workflow.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_integration/test_restore_roundtrips.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_integration/test_stalled_and_acceptance.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_states/__init__.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_states/test_compact_for_llm.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_states/test_critic_enforcer.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_states/test_critic_review_cap.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_states/test_emission_map.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_states/test_executing_metadata.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_states/test_executing_prefix_stability.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_states/test_executing_review_routing.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_states/test_planning_artifact.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_states/test_planning_metadata.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_states/test_progress_ledger.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_states/test_selection.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_states/test_size_compaction_wiring.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_states/test_stall_recovery.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_strategies/__init__.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_strategies/test_identity_block.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_strategies/test_llm_generate_span.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_strategies/test_llm_providers.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_strategies/test_llm_watchdog.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_strategies/test_prepare_history_contract.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_strategies/test_qwen_retry.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_strategies/test_qwen_strategy.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_strategies/test_qwen_truncation.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_strategies/test_qwen_workspace_envelope.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_strategies/test_scripted_llm_contract.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_strategies/test_telemetry_attributes.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_strategies/test_turn_budget_block.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_tools/__init__.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_tools/test_bundle_and_restore.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_tools/test_delegation_identity.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_tools/test_edit_blocks.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_tools/test_fake_executor.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_tools/test_handover_sidecars.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_tools/test_load_handover_resolution.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_tools/test_output_refs.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_tools/test_sandbox_import_surface.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_tools/test_send_actions_error.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_tools/test_shell_executing_loop.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_tools/test_shell_tools.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_tools/test_subagent_failure_contract.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_tools/test_subagent_handover.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_tools/test_three_tier_tools.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_tools/test_workspace_actions.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tools/__init__.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tools/actions/__init__.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tools/actions/base.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tools/actions/final_answer.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tools/actions/load_handover.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tools/actions/visit_page_action.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tools/actions/web_search_action.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tools/actions/wikipedia_search_action.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tools/base.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tools/execution.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tools/functions/__init__.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tools/functions/base.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tools/functions/hitl.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tools/models.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tools/platform/__init__.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tools/platform/base.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tools/platform/edit_blocks.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tools/platform/files_ops.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tools/platform/python_executor.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tools/platform/shell.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tools/platform/subagent.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tools/platform/web_search.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tools/routing.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/trace.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/utils.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/common/__init__.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/common/codec.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/common/errors.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/common/models.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/common/output_window.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/common/tests/__init__.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/common/tests/fake_vm.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/common/tests/fixtures.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/common/tests/test_codec_channel.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/common/tests/test_output_window.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/common/tests/utils.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/logging/__init__.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/logging/formatters.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/logging/processors.py +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate_agent.egg-info/dependency_links.txt +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate_agent.egg-info/entry_points.txt +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate_agent.egg-info/requires.txt +0 -0
- {aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate_agent.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aisolate-agent
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: AI Agent with intelligent planning and reasoning
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -30,7 +30,7 @@ AI Agent with intelligent planning and reasoning
|
|
|
30
30
|
## Local Installation
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
-
pip install path/to/aisolate_agent-0.3.
|
|
33
|
+
pip install path/to/aisolate_agent-0.3.2-py3-none-any.whl
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
## Usage
|
|
@@ -16,6 +16,7 @@ from .core.graph import AgentGraph
|
|
|
16
16
|
from .core.protocols import LLMStrategy, MemoryStrategy
|
|
17
17
|
from .core.snapshot import AgentSnapshot
|
|
18
18
|
from .core.budget import RunBudget
|
|
19
|
+
from .core.initial_state import InitialState
|
|
19
20
|
from .core.events import (
|
|
20
21
|
AgentStartedEvent,
|
|
21
22
|
TurnStartedEvent,
|
|
@@ -316,6 +317,7 @@ class Agent:
|
|
|
316
317
|
self, task: str, conversation_id: str,
|
|
317
318
|
*, cancel_event: "Optional[asyncio.Event]" = None,
|
|
318
319
|
budget: Optional[RunBudget] = None,
|
|
320
|
+
initial_state: "Optional[InitialState]" = None,
|
|
319
321
|
):
|
|
320
322
|
"""Run the agent with streaming updates.
|
|
321
323
|
|
|
@@ -336,6 +338,10 @@ class Agent:
|
|
|
336
338
|
``cancel_event``, with reason ``time_budget`` /
|
|
337
339
|
``token_budget``. Both signals live on the root context;
|
|
338
340
|
sub loops observe them through ``suspension_due()``.
|
|
341
|
+
initial_state: Log events and workspace files to seed beside the
|
|
342
|
+
task. Applied at CYCLE start only — a fresh run or a follow-up
|
|
343
|
+
on a finished conversation — and ignored on a mid-run resume,
|
|
344
|
+
which already holds its state.
|
|
339
345
|
|
|
340
346
|
Yields:
|
|
341
347
|
Dict updates about agent state, tool calls, and progress
|
|
@@ -398,6 +404,7 @@ class Agent:
|
|
|
398
404
|
# through suspension_due() → root_context().
|
|
399
405
|
context.cancel_event = cancel_event
|
|
400
406
|
context.budget = budget
|
|
407
|
+
context.initial_state = initial_state
|
|
401
408
|
|
|
402
409
|
# RESTORATION LOGIC - Load and apply snapshot if it exists
|
|
403
410
|
snapshot = await context.memory.load_latest_snapshot(conversation_id)
|
|
@@ -1392,6 +1399,13 @@ class Agent:
|
|
|
1392
1399
|
"turn": context.turn,
|
|
1393
1400
|
"suspended_at": datetime.now(timezone.utc).isoformat(),
|
|
1394
1401
|
}
|
|
1402
|
+
if reason == "turn_budget":
|
|
1403
|
+
# The cap that bound the run, so a caller can raise it without
|
|
1404
|
+
# knowing how this agent was configured. Resuming a turn-budget
|
|
1405
|
+
# suspension with an unchanged cap re-suspends without executing a
|
|
1406
|
+
# turn (see the check in the run loop), which is indistinguishable
|
|
1407
|
+
# from a run that tried and got nowhere.
|
|
1408
|
+
context.suspension["max_turns"] = self.settings.max_turns
|
|
1395
1409
|
if reason == "hitl" and context.pending_hitl_signal is not None:
|
|
1396
1410
|
context.suspension["signal"] = dict(
|
|
1397
1411
|
context.pending_hitl_signal.__dict__
|
|
@@ -1596,11 +1610,17 @@ class Agent:
|
|
|
1596
1610
|
# Each state projects its own prompt envelope from the log.
|
|
1597
1611
|
if not context.conversation.events():
|
|
1598
1612
|
context.seed_task()
|
|
1613
|
+
context.apply_initial_state()
|
|
1599
1614
|
|
|
1600
1615
|
# Enter the graph's entry node (only if not restored from snapshot)
|
|
1601
1616
|
if context.execution.current_state is None:
|
|
1602
1617
|
context.enter_node(self.graph.entry)
|
|
1603
1618
|
|
|
1619
|
+
# Where this run picked up the counter. A resumed run that suspends
|
|
1620
|
+
# again having executed nothing did not try and fail — its budget
|
|
1621
|
+
# was never raised.
|
|
1622
|
+
turn_at_entry = context.turn
|
|
1623
|
+
|
|
1604
1624
|
yield AgentStartedEvent(
|
|
1605
1625
|
task=context.task,
|
|
1606
1626
|
turn=context.turn,
|
|
@@ -1641,9 +1661,12 @@ class Agent:
|
|
|
1641
1661
|
):
|
|
1642
1662
|
suspend_reason = "turn_budget"
|
|
1643
1663
|
bound_logger.warning(
|
|
1644
|
-
"Turn budget exhausted — suspending (resumable)"
|
|
1664
|
+
"Turn budget exhausted — suspending (resumable). "
|
|
1665
|
+
"A resume must RAISE max_turns; an unchanged cap "
|
|
1666
|
+
"re-suspends here without executing a turn.",
|
|
1645
1667
|
turn=context.turn,
|
|
1646
1668
|
max_turns=self.settings.max_turns,
|
|
1669
|
+
executed_this_run=context.turn - turn_at_entry,
|
|
1647
1670
|
)
|
|
1648
1671
|
break
|
|
1649
1672
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Core agent module containing foundational components.
|
|
2
|
+
|
|
3
|
+
Alongside the strategy protocols and the run context, this exports the workflow
|
|
4
|
+
composition surface — ``AgentGraph``, its routing/validation types, and the
|
|
5
|
+
``policy`` routers — so a consumer can compose its own control flow from the
|
|
6
|
+
same parts ``aisolate.agent.graphs`` uses.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from . import policy
|
|
10
|
+
from .protocols import LLMStrategy, MemoryStrategy, Tool
|
|
11
|
+
from .exceptions import AgentError, StateTransitionError, ToolExecutionError
|
|
12
|
+
from .context import AgentContext
|
|
13
|
+
from .graph import (
|
|
14
|
+
AgentGraph,
|
|
15
|
+
GraphValidationError,
|
|
16
|
+
Router,
|
|
17
|
+
RoutingDecision,
|
|
18
|
+
RoutingError,
|
|
19
|
+
)
|
|
20
|
+
from .initial_state import InitialState, WorkspaceSeed
|
|
21
|
+
from .llm_response import TokenUsage, LLMResponse
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"LLMStrategy",
|
|
25
|
+
"MemoryStrategy",
|
|
26
|
+
"Tool",
|
|
27
|
+
"AgentError",
|
|
28
|
+
"StateTransitionError",
|
|
29
|
+
"ToolExecutionError",
|
|
30
|
+
"AgentContext",
|
|
31
|
+
"AgentGraph",
|
|
32
|
+
"InitialState",
|
|
33
|
+
"WorkspaceSeed",
|
|
34
|
+
"GraphValidationError",
|
|
35
|
+
"Router",
|
|
36
|
+
"RoutingDecision",
|
|
37
|
+
"RoutingError",
|
|
38
|
+
"policy",
|
|
39
|
+
"TokenUsage",
|
|
40
|
+
"LLMResponse",
|
|
41
|
+
]
|
|
@@ -42,6 +42,7 @@ if TYPE_CHECKING: # pragma: no cover
|
|
|
42
42
|
from ..tools.models import FinalAnswerResult
|
|
43
43
|
from .budget import RunBudget
|
|
44
44
|
from .graph import AgentGraph, RoutingDecision
|
|
45
|
+
from .initial_state import InitialState
|
|
45
46
|
|
|
46
47
|
logger = get_logger("aisolate.agent.core.context")
|
|
47
48
|
|
|
@@ -304,6 +305,10 @@ class AgentContext:
|
|
|
304
305
|
self.current_node: Optional[str] = None
|
|
305
306
|
self._node_instances: Dict[str, "State"] = {}
|
|
306
307
|
|
|
308
|
+
# Caller-supplied opening state, applied at cycle start only (see
|
|
309
|
+
# apply_initial_state).
|
|
310
|
+
self.initial_state: Optional["InitialState"] = None
|
|
311
|
+
|
|
307
312
|
# Review posture. ``review_count`` is derived from the log (see the
|
|
308
313
|
# property); only the budget is a plain field.
|
|
309
314
|
self.max_reviews: int = 2
|
|
@@ -533,6 +538,24 @@ class AgentContext:
|
|
|
533
538
|
)
|
|
534
539
|
)
|
|
535
540
|
|
|
541
|
+
def apply_initial_state(self) -> None:
|
|
542
|
+
"""Apply the caller's supplied opening state, if any.
|
|
543
|
+
|
|
544
|
+
Called beside ``seed_task`` — the two cycle starts, a fresh run and a
|
|
545
|
+
follow-up — and nowhere else: a mid-run resume already holds this state
|
|
546
|
+
and re-applying it would overwrite live work. Files OVERLAY the
|
|
547
|
+
manifest, so a follow-up's restored artifacts survive.
|
|
548
|
+
"""
|
|
549
|
+
state = self.initial_state
|
|
550
|
+
if not state:
|
|
551
|
+
return
|
|
552
|
+
for event in state.events:
|
|
553
|
+
self.conversation.append(event)
|
|
554
|
+
for seed in state.files:
|
|
555
|
+
self.artifacts.put_text(
|
|
556
|
+
seed.path, seed.text, kind=seed.kind, mime=seed.mime,
|
|
557
|
+
)
|
|
558
|
+
|
|
536
559
|
def active_view(self) -> List[Message]:
|
|
537
560
|
"""The current state's projected LLM input (its curated view of the log).
|
|
538
561
|
|
|
@@ -719,9 +742,10 @@ class AgentContext:
|
|
|
719
742
|
message is appended to the log as a ``steering`` event — binding
|
|
720
743
|
user input for the remaining work, NOT a new task;
|
|
721
744
|
* a finished cycle → a follow-up: the new user message becomes a new
|
|
722
|
-
``task`` event and the run starts at
|
|
723
|
-
|
|
724
|
-
the
|
|
745
|
+
``task`` event and the run starts at the graph's ``cycle_entry``
|
|
746
|
+
(``planning`` for both shipped shapes) on a FRESH turn budget —
|
|
747
|
+
planning's view seeds it with the latest compaction (or the
|
|
748
|
+
unsuperseded prior final);
|
|
725
749
|
* unfinished with NO posture → crash-degraded: the same mid-run
|
|
726
750
|
resume at ``current_node``, plus a synthetic note that the run was
|
|
727
751
|
interrupted uncleanly (kernel state may be lost).
|
|
@@ -877,7 +901,8 @@ class AgentContext:
|
|
|
877
901
|
),
|
|
878
902
|
))
|
|
879
903
|
self.seed_task(self._initial_task)
|
|
880
|
-
self.
|
|
904
|
+
self.apply_initial_state()
|
|
905
|
+
self.enter_node(self.graph.cycle_entry)
|
|
881
906
|
return
|
|
882
907
|
|
|
883
908
|
# Suspended (stop / time_budget / token_budget) or crash-degraded
|
|
@@ -134,6 +134,11 @@ META_KEYS = frozenset(
|
|
|
134
134
|
"omitted_count",
|
|
135
135
|
"artifact_key",
|
|
136
136
|
"tool_call_id",
|
|
137
|
+
# Who authored the event, when that is not the state whose phase it
|
|
138
|
+
# carries: "supplied" marks one the caller seeded at cycle start
|
|
139
|
+
# (see core.initial_state), which reads and renders exactly like a
|
|
140
|
+
# generated one and must stay distinguishable in a trace.
|
|
141
|
+
"origin",
|
|
137
142
|
}
|
|
138
143
|
)
|
|
139
144
|
|
|
@@ -5,10 +5,17 @@ An ``AgentGraph`` is the whole control-flow story of an agent: named nodes
|
|
|
5
5
|
``(node, Output type)``, and — where a transition is a decision rather than a
|
|
6
6
|
constant — a pure policy router that returns a ``RoutingDecision``.
|
|
7
7
|
|
|
8
|
+
Everything the framework needs to know about a workflow is DECLARED here, not
|
|
9
|
+
inferred elsewhere from node names: where a run starts (``entry``), where a new
|
|
10
|
+
cycle starts (``cycle_entry``), and whether the run maintains the workspace
|
|
11
|
+
checklist (``workspace_ops``). A consumer composing its own graph therefore gets
|
|
12
|
+
the same behaviour the shipped shapes get, without the framework having to
|
|
13
|
+
recognise it.
|
|
14
|
+
|
|
8
15
|
The graph is validated at construction, so an incoherent workflow fails when
|
|
9
16
|
it is composed, not turns later mid-run:
|
|
10
17
|
|
|
11
|
-
* the entry
|
|
18
|
+
* the entry and cycle-entry nodes exist; every edge references known nodes;
|
|
12
19
|
* every output a node declares in ``produces`` has a route;
|
|
13
20
|
* at least one node is terminal (the run can end);
|
|
14
21
|
* the view lint: every kind a node declares in ``emits`` is viewed by some
|
|
@@ -80,6 +87,17 @@ class AgentGraph:
|
|
|
80
87
|
entry: str
|
|
81
88
|
nodes: Mapping[str, Callable[[], "State"]]
|
|
82
89
|
edges: Mapping[Tuple[str, Type["Output"]], Union[str, Router]]
|
|
90
|
+
#: Where a NEW CYCLE starts — a follow-up request on a finished
|
|
91
|
+
#: conversation. Distinct from ``entry`` because intake nodes run once per
|
|
92
|
+
#: conversation, not once per cycle: the standard graph enters at
|
|
93
|
+
#: ``clarification`` and re-enters at ``planning``. Defaults to ``entry``.
|
|
94
|
+
cycle_entry: Optional[str] = None
|
|
95
|
+
#: Whether this workflow maintains the workspace checklist and working
|
|
96
|
+
#: memory (``task.md`` / ``memory.md``, via the ``mark_tasks`` /
|
|
97
|
+
#: ``write_memory`` ops). Defaults to ``"task" in nodes`` — the node that
|
|
98
|
+
#: generates a checklist implies one — so a graph that obtains its
|
|
99
|
+
#: checklist another way declares it instead.
|
|
100
|
+
workspace_ops: Optional[bool] = None
|
|
83
101
|
# One prototype instance per node, built during validation. Used for
|
|
84
102
|
# declaration lookups (produces/emits/view/terminal) — never for runs.
|
|
85
103
|
_prototypes: Dict[str, "State"] = field(
|
|
@@ -89,6 +107,10 @@ class AgentGraph:
|
|
|
89
107
|
def __post_init__(self) -> None:
|
|
90
108
|
object.__setattr__(self, "nodes", dict(self.nodes))
|
|
91
109
|
object.__setattr__(self, "edges", dict(self.edges))
|
|
110
|
+
if self.cycle_entry is None:
|
|
111
|
+
object.__setattr__(self, "cycle_entry", self.entry)
|
|
112
|
+
if self.workspace_ops is None:
|
|
113
|
+
object.__setattr__(self, "workspace_ops", "task" in self.nodes)
|
|
92
114
|
self._validate()
|
|
93
115
|
|
|
94
116
|
# ---- routing -------------------------------------------------------------
|
|
@@ -130,6 +152,11 @@ class AgentGraph:
|
|
|
130
152
|
raise GraphValidationError(
|
|
131
153
|
f"entry node {self.entry!r} is not in nodes {sorted(self.nodes)}"
|
|
132
154
|
)
|
|
155
|
+
if self.cycle_entry not in self.nodes:
|
|
156
|
+
raise GraphValidationError(
|
|
157
|
+
f"cycle_entry node {self.cycle_entry!r} is not in nodes "
|
|
158
|
+
f"{sorted(self.nodes)}"
|
|
159
|
+
)
|
|
133
160
|
|
|
134
161
|
for name, factory in self.nodes.items():
|
|
135
162
|
try:
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""State a caller supplies at cycle start, beside the task.
|
|
2
|
+
|
|
3
|
+
A run's opening state had exactly one producer: the memory strategy's latest
|
|
4
|
+
snapshot for the conversation. The task, which is also opening state, was
|
|
5
|
+
already an argument — this makes the rest one too, so a caller that knows the
|
|
6
|
+
plan (a pipeline whose stage prompt IS the plan), or that wants to fork a
|
|
7
|
+
conversation, replay from a known state, or construct a mid-cycle posture in a
|
|
8
|
+
test, can say so instead of writing a snapshot first.
|
|
9
|
+
|
|
10
|
+
Applied where a CYCLE starts — a fresh run, or a follow-up on a finished
|
|
11
|
+
conversation — and ignored on a mid-run resume, whose state is already live and
|
|
12
|
+
must not be overwritten. ``events`` append to the log in order; ``files``
|
|
13
|
+
overlay the workspace manifest, so a follow-up's restored artifacts survive and
|
|
14
|
+
named slots are replaced.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from dataclasses import dataclass, field
|
|
20
|
+
from typing import TYPE_CHECKING, Optional, Tuple
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from .conversation_log import Event
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(frozen=True)
|
|
27
|
+
class WorkspaceSeed:
|
|
28
|
+
"""One workspace file to place before the first turn.
|
|
29
|
+
|
|
30
|
+
``kind`` is the framework's internal role for its reserved slots (see
|
|
31
|
+
``core.workspace.named_kind``); ``None`` for an ordinary file.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
path: str
|
|
35
|
+
text: str
|
|
36
|
+
kind: Optional[str] = None
|
|
37
|
+
mime: str = "text/markdown"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass(frozen=True)
|
|
41
|
+
class InitialState:
|
|
42
|
+
"""Log events and workspace files to seed at cycle start."""
|
|
43
|
+
|
|
44
|
+
events: Tuple["Event", ...] = field(default_factory=tuple)
|
|
45
|
+
files: Tuple[WorkspaceSeed, ...] = field(default_factory=tuple)
|
|
46
|
+
|
|
47
|
+
def __bool__(self) -> bool:
|
|
48
|
+
return bool(self.events or self.files)
|
|
@@ -13,7 +13,8 @@ judges the handover), no compaction (the bounded output IS the handover), and
|
|
|
13
13
|
therefore no HITL at all.
|
|
14
14
|
|
|
15
15
|
Composition facts, not flags: an agent reviews iff the graph has a ``critic``
|
|
16
|
-
node; a graph without clarification starts at planning.
|
|
16
|
+
node; a graph without clarification starts at planning. Both shapes declare
|
|
17
|
+
``cycle_entry="planning"`` — a follow-up skips intake. The workflow is
|
|
17
18
|
substrate-independent — the same shapes run on the jupyter and shell bindings
|
|
18
19
|
(see ``aisolate.agent.substrate``).
|
|
19
20
|
|
|
@@ -108,7 +109,9 @@ def build_workflow(
|
|
|
108
109
|
if role == "sub":
|
|
109
110
|
# The bounded output IS the handover — no review, no compaction.
|
|
110
111
|
edges[("executing", FinalDrafted)] = "finished"
|
|
111
|
-
return AgentGraph(
|
|
112
|
+
return AgentGraph(
|
|
113
|
+
entry=entry, nodes=nodes, edges=edges, cycle_entry="planning",
|
|
114
|
+
)
|
|
112
115
|
|
|
113
116
|
nodes["compaction"] = CompactionState
|
|
114
117
|
edges[("executing", FinalDrafted)] = policy.route_after_final
|
|
@@ -129,7 +132,11 @@ def build_workflow(
|
|
|
129
132
|
edges[("critic", VerdictProduced)] = policy.route_after_verdict
|
|
130
133
|
edges[("critic", ReviewFailed)] = policy.route_after_review_failure
|
|
131
134
|
|
|
132
|
-
|
|
135
|
+
# A follow-up re-enters at planning, never at clarification: intake happens
|
|
136
|
+
# once per conversation, and the human is not asked to restate the brief.
|
|
137
|
+
return AgentGraph(
|
|
138
|
+
entry=entry, nodes=nodes, edges=edges, cycle_entry="planning",
|
|
139
|
+
)
|
|
133
140
|
|
|
134
141
|
|
|
135
142
|
STANDARD_GRAPH = build_workflow()
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""States module for agent lifecycle management.
|
|
2
|
+
|
|
3
|
+
This is the supported surface for composing a workflow: the node classes, plus
|
|
4
|
+
the ``Output`` types that key an ``AgentGraph``'s edges. Compose them with
|
|
5
|
+
``aisolate.agent.core.AgentGraph`` and the routers in
|
|
6
|
+
``aisolate.agent.core.policy``; ``aisolate.agent.graphs.build_workflow`` is one
|
|
7
|
+
convenience constructor over the same parts, not the only way in.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from .base import AbstractState, State, StateResult
|
|
11
|
+
from .clarification import ClarificationState
|
|
12
|
+
from .compaction import CompactionState
|
|
13
|
+
from .critic import CriticState
|
|
14
|
+
from .executing import ExecutingState
|
|
15
|
+
from .expert_synthesis import ExpertSynthesisState
|
|
16
|
+
from .finished import FinishedState
|
|
17
|
+
from .hitl_pause import HITLPauseState
|
|
18
|
+
from .planning import PlanningState
|
|
19
|
+
from .task import TaskState
|
|
20
|
+
from .outputs import (
|
|
21
|
+
BriefProduced,
|
|
22
|
+
CompactionFailed,
|
|
23
|
+
CompactionWritten,
|
|
24
|
+
FinalDrafted,
|
|
25
|
+
IntakeComplete,
|
|
26
|
+
NoAction,
|
|
27
|
+
Output,
|
|
28
|
+
PlanProduced,
|
|
29
|
+
QuestionAsked,
|
|
30
|
+
ReviewFailed,
|
|
31
|
+
Stalled,
|
|
32
|
+
TasksProduced,
|
|
33
|
+
ToolExecuted,
|
|
34
|
+
VerdictProduced,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
__all__ = [
|
|
38
|
+
"AbstractState",
|
|
39
|
+
"State",
|
|
40
|
+
"StateResult",
|
|
41
|
+
"ClarificationState",
|
|
42
|
+
"CompactionState",
|
|
43
|
+
"CriticState",
|
|
44
|
+
"ExecutingState",
|
|
45
|
+
"ExpertSynthesisState",
|
|
46
|
+
"FinishedState",
|
|
47
|
+
"HITLPauseState",
|
|
48
|
+
"PlanningState",
|
|
49
|
+
"TaskState",
|
|
50
|
+
"Output",
|
|
51
|
+
"BriefProduced",
|
|
52
|
+
"CompactionFailed",
|
|
53
|
+
"CompactionWritten",
|
|
54
|
+
"FinalDrafted",
|
|
55
|
+
"IntakeComplete",
|
|
56
|
+
"NoAction",
|
|
57
|
+
"PlanProduced",
|
|
58
|
+
"QuestionAsked",
|
|
59
|
+
"ReviewFailed",
|
|
60
|
+
"Stalled",
|
|
61
|
+
"TasksProduced",
|
|
62
|
+
"ToolExecuted",
|
|
63
|
+
"VerdictProduced",
|
|
64
|
+
]
|
|
@@ -148,7 +148,7 @@ class ExecutionSubstrate:
|
|
|
148
148
|
|
|
149
149
|
A sub-agent runs the forced lean workflow, not the graph its Agent
|
|
150
150
|
was constructed against — so ops gated on the graph (the workspace
|
|
151
|
-
ops) are re-derived here against the sub graph. Returns
|
|
151
|
+
ops it declares) are re-derived here against the sub graph. Returns
|
|
152
152
|
``(extra_actions, extra_tools)``: kernel CodeActions on jupyter,
|
|
153
153
|
host-side tools on shell.
|
|
154
154
|
"""
|
|
@@ -208,11 +208,11 @@ class JupyterSubstrate(ExecutionSubstrate):
|
|
|
208
208
|
|
|
209
209
|
actions["load_handover_data"] = LoadHandoverAction()
|
|
210
210
|
|
|
211
|
-
#
|
|
212
|
-
#
|
|
213
|
-
#
|
|
211
|
+
# A workflow that maintains task.md / memory.md gets the ops that
|
|
212
|
+
# maintain them (the graph declares this; it is not read off the node
|
|
213
|
+
# set, so a supplied checklist keeps its tools).
|
|
214
214
|
graph = self.agent.graph
|
|
215
|
-
if graph is not None and
|
|
215
|
+
if graph is not None and getattr(graph, "workspace_ops", False):
|
|
216
216
|
from .tools.actions.workspace import MarkTasksAction, WriteMemoryAction
|
|
217
217
|
|
|
218
218
|
actions["mark_tasks"] = MarkTasksAction()
|
|
@@ -221,7 +221,7 @@ class JupyterSubstrate(ExecutionSubstrate):
|
|
|
221
221
|
return actions
|
|
222
222
|
|
|
223
223
|
def delegation_ops(self, sub_graph) -> "tuple[Dict[str, Any], List[Any]]":
|
|
224
|
-
if sub_graph is None or
|
|
224
|
+
if sub_graph is None or not getattr(sub_graph, "workspace_ops", False):
|
|
225
225
|
return {}, []
|
|
226
226
|
from .tools.actions.workspace import MarkTasksAction, WriteMemoryAction
|
|
227
227
|
|
|
@@ -352,7 +352,7 @@ class ShellSubstrate(ExecutionSubstrate):
|
|
|
352
352
|
ShellFinalAnswerTool(),
|
|
353
353
|
]
|
|
354
354
|
graph = self.agent.graph
|
|
355
|
-
if graph is not None and
|
|
355
|
+
if graph is not None and getattr(graph, "workspace_ops", False):
|
|
356
356
|
tools.append(MarkTasksTool())
|
|
357
357
|
tools.append(WriteMemoryTool())
|
|
358
358
|
for tool in tools:
|
|
@@ -365,7 +365,7 @@ class ShellSubstrate(ExecutionSubstrate):
|
|
|
365
365
|
return {}
|
|
366
366
|
|
|
367
367
|
def delegation_ops(self, sub_graph) -> "tuple[Dict[str, Any], List[Any]]":
|
|
368
|
-
if sub_graph is None or
|
|
368
|
+
if sub_graph is None or not getattr(sub_graph, "workspace_ops", False):
|
|
369
369
|
return {}, []
|
|
370
370
|
from .tools.platform.workspace_tools import MarkTasksTool, WriteMemoryTool
|
|
371
371
|
|
|
@@ -20,6 +20,43 @@ def test_canonical_message_is_harmony_primitive():
|
|
|
20
20
|
assert Message is openai_harmony.Message
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
# ── The composition surface is complete ──────────────────────────────────────────
|
|
24
|
+
def test_every_state_class_is_exported():
|
|
25
|
+
"""A node a consumer cannot import from ``aisolate.agent.states`` is a node
|
|
26
|
+
it cannot compose a graph with. ``TaskState`` was missing for exactly as
|
|
27
|
+
long as nothing checked."""
|
|
28
|
+
import importlib
|
|
29
|
+
import inspect
|
|
30
|
+
import pkgutil
|
|
31
|
+
|
|
32
|
+
import aisolate.agent.states as states_pkg
|
|
33
|
+
|
|
34
|
+
discovered = set()
|
|
35
|
+
for mod in pkgutil.iter_modules(states_pkg.__path__):
|
|
36
|
+
module = importlib.import_module(f"{states_pkg.__name__}.{mod.name}")
|
|
37
|
+
for name, obj in vars(module).items():
|
|
38
|
+
if (
|
|
39
|
+
inspect.isclass(obj)
|
|
40
|
+
and issubclass(obj, State)
|
|
41
|
+
and obj.__module__ == module.__name__
|
|
42
|
+
and not name.startswith("_")
|
|
43
|
+
):
|
|
44
|
+
discovered.add(name)
|
|
45
|
+
|
|
46
|
+
missing = discovered - set(states_pkg.__all__)
|
|
47
|
+
assert not missing, f"states/__init__.py does not export: {sorted(missing)}"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_graph_composition_parts_are_exported_from_core():
|
|
51
|
+
"""``AgentGraph`` + the policy routers are the composition kit: a consumer
|
|
52
|
+
building its own workflow must not have to reach past the package."""
|
|
53
|
+
import aisolate.agent.core as core
|
|
54
|
+
|
|
55
|
+
for name in ("AgentGraph", "RoutingDecision", "policy"):
|
|
56
|
+
assert name in core.__all__
|
|
57
|
+
assert hasattr(core, name)
|
|
58
|
+
|
|
59
|
+
|
|
23
60
|
# ── Memory is an append-only log ──────────────────────────────────────────────────
|
|
24
61
|
def test_agent_context_has_no_destructive_history_replace():
|
|
25
62
|
"""``AgentContext`` exposes no whole-history replacement or direct
|
{aisolate_agent-0.3.1 → aisolate_agent-0.3.2}/src/aisolate/agent/tests/test_core/test_graph.py
RENAMED
|
@@ -104,6 +104,12 @@ class TestValidator:
|
|
|
104
104
|
with pytest.raises(GraphValidationError, match="entry"):
|
|
105
105
|
minimal_graph(entry="nowhere")
|
|
106
106
|
|
|
107
|
+
def test_unknown_cycle_entry_raises(self):
|
|
108
|
+
# Validated exactly like `entry`: a follow-up must not discover
|
|
109
|
+
# mid-run that its re-entry node does not exist.
|
|
110
|
+
with pytest.raises(GraphValidationError, match="cycle_entry"):
|
|
111
|
+
minimal_graph(cycle_entry="nowhere")
|
|
112
|
+
|
|
107
113
|
def test_edge_targeting_unknown_node_raises(self):
|
|
108
114
|
with pytest.raises(GraphValidationError, match="unknown"):
|
|
109
115
|
minimal_graph(
|
|
@@ -344,6 +350,80 @@ class TestInstantiation:
|
|
|
344
350
|
)
|
|
345
351
|
|
|
346
352
|
|
|
353
|
+
# ── 4b. Declarations: cycle entry & workspace posture ────────────────────────
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
class TaskNamedStub(State):
|
|
357
|
+
"""A node registered under the reserved ``task`` name."""
|
|
358
|
+
|
|
359
|
+
phase = "task"
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
class TestCycleEntry:
|
|
363
|
+
"""Where a NEW cycle starts is declared, not inferred from a node name."""
|
|
364
|
+
|
|
365
|
+
def test_defaults_to_entry(self):
|
|
366
|
+
assert minimal_graph().cycle_entry == "start"
|
|
367
|
+
|
|
368
|
+
def test_explicit_value_is_kept(self):
|
|
369
|
+
assert minimal_graph(cycle_entry="end").cycle_entry == "end"
|
|
370
|
+
|
|
371
|
+
@pytest.mark.parametrize(
|
|
372
|
+
"kwargs",
|
|
373
|
+
[
|
|
374
|
+
{},
|
|
375
|
+
{"clarification": False, "review": False},
|
|
376
|
+
{"clarification": False, "review": False, "tasks": True},
|
|
377
|
+
{"role": "sub"},
|
|
378
|
+
],
|
|
379
|
+
)
|
|
380
|
+
def test_shipped_shapes_re_enter_at_planning(self, kwargs):
|
|
381
|
+
# A follow-up skips intake: the standard graph enters at
|
|
382
|
+
# `clarification` but re-enters at `planning`. This was a literal in
|
|
383
|
+
# the restore path; every shipped shape must keep that behaviour.
|
|
384
|
+
graph = build_workflow(**kwargs)
|
|
385
|
+
assert graph.cycle_entry == "planning"
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
class TestWorkspaceOps:
|
|
389
|
+
"""Whether the run maintains task.md / memory.md is declared, and its
|
|
390
|
+
default reproduces the ``"task" in nodes`` proxy it replaces."""
|
|
391
|
+
|
|
392
|
+
def test_defaults_true_when_a_task_node_is_present(self):
|
|
393
|
+
graph = minimal_graph(
|
|
394
|
+
nodes={"start": SilentStub, "task": TaskNamedStub, "end": TerminalStub},
|
|
395
|
+
)
|
|
396
|
+
assert graph.workspace_ops is True
|
|
397
|
+
|
|
398
|
+
def test_defaults_false_without_a_task_node(self):
|
|
399
|
+
assert minimal_graph().workspace_ops is False
|
|
400
|
+
|
|
401
|
+
def test_can_be_declared_without_a_task_node(self):
|
|
402
|
+
# The case the proxy could not express: a graph that is handed its
|
|
403
|
+
# checklist instead of generating one still needs the ops that work it.
|
|
404
|
+
assert minimal_graph(workspace_ops=True).workspace_ops is True
|
|
405
|
+
|
|
406
|
+
def test_can_be_declined_with_a_task_node(self):
|
|
407
|
+
graph = minimal_graph(
|
|
408
|
+
nodes={"start": SilentStub, "task": TaskNamedStub, "end": TerminalStub},
|
|
409
|
+
workspace_ops=False,
|
|
410
|
+
)
|
|
411
|
+
assert graph.workspace_ops is False
|
|
412
|
+
|
|
413
|
+
@pytest.mark.parametrize(
|
|
414
|
+
"kwargs",
|
|
415
|
+
[
|
|
416
|
+
{},
|
|
417
|
+
{"clarification": False, "review": False},
|
|
418
|
+
{"clarification": False, "review": False, "tasks": True},
|
|
419
|
+
{"role": "sub"},
|
|
420
|
+
],
|
|
421
|
+
)
|
|
422
|
+
def test_shipped_shapes_match_the_replaced_proxy(self, kwargs):
|
|
423
|
+
graph = build_workflow(**kwargs)
|
|
424
|
+
assert graph.workspace_ops == ("task" in graph.nodes)
|
|
425
|
+
|
|
426
|
+
|
|
347
427
|
# ── 5. Extension: a custom graph runs end-to-end on the unmodified driver ────
|
|
348
428
|
|
|
349
429
|
|