aisolate-agent 0.3.4__tar.gz → 0.3.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/PKG-INFO +2 -2
  2. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/README.md +1 -1
  3. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/pyproject.toml +1 -1
  4. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/agent.py +3 -0
  5. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/config/agent_settings.py +8 -0
  6. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/core/context.py +5 -0
  7. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/core/spine_compaction.py +7 -0
  8. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/states/executing.py +155 -19
  9. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_spine_compaction.py +14 -0
  10. aisolate_agent-0.3.6/src/aisolate/agent/tests/test_package_import_surface.py +30 -0
  11. aisolate_agent-0.3.6/src/aisolate/agent/tests/test_states/test_progress_ledger.py +367 -0
  12. aisolate_agent-0.3.6/src/aisolate/agent/tests/test_tools/test_python_executor_window.py +84 -0
  13. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_tools/test_subagent_failure_contract.py +1 -0
  14. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tools/platform/python_executor.py +23 -11
  15. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tools/platform/subagent.py +3 -0
  16. aisolate_agent-0.3.6/src/aisolate/common/execution_protocol.py +3 -0
  17. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate_agent.egg-info/PKG-INFO +2 -2
  18. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate_agent.egg-info/SOURCES.txt +3 -0
  19. aisolate_agent-0.3.4/src/aisolate/agent/tests/test_states/test_progress_ledger.py +0 -190
  20. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/setup.cfg +0 -0
  21. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/__init__.py +0 -0
  22. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/__init__.py +0 -0
  23. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/config/__init__.py +0 -0
  24. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/config/prompts/__init__.py +0 -0
  25. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/config/state_llm.py +0 -0
  26. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/core/__init__.py +0 -0
  27. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/core/artifacts.py +0 -0
  28. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/core/budget.py +0 -0
  29. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/core/conversation_log.py +0 -0
  30. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/core/events.py +0 -0
  31. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/core/exceptions.py +0 -0
  32. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/core/graph.py +0 -0
  33. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/core/initial_state.py +0 -0
  34. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/core/llm_response.py +0 -0
  35. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/core/policy.py +0 -0
  36. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/core/protocols.py +0 -0
  37. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/core/snapshot.py +0 -0
  38. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/core/tests/test_context_snapshots.py +0 -0
  39. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/core/tests/test_snapshot.py +0 -0
  40. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/core/views.py +0 -0
  41. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/core/workspace.py +0 -0
  42. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/graphs.py +0 -0
  43. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/logging/__init__.py +0 -0
  44. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/logging/config.py +0 -0
  45. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/logging/factory.py +0 -0
  46. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/logging/unified_logger.py +0 -0
  47. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/main.py +0 -0
  48. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/memory/__init__.py +0 -0
  49. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/memory/models.py +0 -0
  50. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/states/__init__.py +0 -0
  51. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/states/base.py +0 -0
  52. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/states/clarification.py +0 -0
  53. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/states/compaction.py +0 -0
  54. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/states/critic.py +0 -0
  55. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/states/expert_synthesis.py +0 -0
  56. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/states/finished.py +0 -0
  57. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/states/hitl_pause.py +0 -0
  58. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/states/outputs.py +0 -0
  59. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/states/planning.py +0 -0
  60. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/states/task.py +0 -0
  61. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/status.py +0 -0
  62. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/strategies/__init__.py +0 -0
  63. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/strategies/llm/__init__.py +0 -0
  64. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/strategies/llm/base.py +0 -0
  65. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/strategies/llm/harmony.py +0 -0
  66. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/strategies/llm/oai.py +0 -0
  67. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/strategies/llm/qwen.py +0 -0
  68. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/strategies/memory/__init__.py +0 -0
  69. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/strategies/memory/in_memory.py +0 -0
  70. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/strategies/memory/tests/test_in_memory.py +0 -0
  71. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/strategies/memory/vector.py +0 -0
  72. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/substrate.py +0 -0
  73. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/telemetry/__init__.py +0 -0
  74. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/telemetry/decorators.py +0 -0
  75. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/telemetry/factory.py +0 -0
  76. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/telemetry/interface.py +0 -0
  77. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/telemetry/noop.py +0 -0
  78. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/telemetry/openinference.py +0 -0
  79. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/telemetry/opentelemetry_impl.py +0 -0
  80. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/telemetry/utils.py +0 -0
  81. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/__init__.py +0 -0
  82. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/conftest.py +0 -0
  83. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/fakes.py +0 -0
  84. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/__init__.py +0 -0
  85. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_architecture_assumptions.py +0 -0
  86. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_artifact_rows.py +0 -0
  87. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_change_bridge_echo.py +0 -0
  88. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_conversation_log.py +0 -0
  89. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_delegated_lineage_restore.py +0 -0
  90. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_event_agent_name_stamping.py +0 -0
  91. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_execute_turn_review_pending.py +0 -0
  92. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_firewall_property.py +0 -0
  93. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_graph.py +0 -0
  94. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_initial_state.py +0 -0
  95. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_legacy_snapshot.py +0 -0
  96. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_live_capture.py +0 -0
  97. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_locale_prelude.py +0 -0
  98. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_policy.py +0 -0
  99. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_restore_review_posture.py +0 -0
  100. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_resume_semantics.py +0 -0
  101. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_run_streaming_cancel.py +0 -0
  102. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_state_llm_overrides.py +0 -0
  103. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_state_transitions.py +0 -0
  104. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_status_summarizer.py +0 -0
  105. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_subagent_graph.py +0 -0
  106. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_substrate.py +0 -0
  107. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_token_accumulation.py +0 -0
  108. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_turn_started_events.py +0 -0
  109. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_turn_usage_isolation.py +0 -0
  110. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_uniform_state.py +0 -0
  111. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_views.py +0 -0
  112. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_workspace_bridge_attribution.py +0 -0
  113. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_workspace_capture.py +0 -0
  114. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_core/test_workspace_changed_event.py +0 -0
  115. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_e2e/__init__.py +0 -0
  116. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_e2e/conftest.py +0 -0
  117. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_e2e/test_delegation_file_paths.py +0 -0
  118. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_e2e/test_delegation_recovery.py +0 -0
  119. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_e2e/test_locale_helpers.py +0 -0
  120. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_e2e/test_size_compaction.py +0 -0
  121. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_e2e/test_stall_clone_96919cf1.py +0 -0
  122. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_e2e/test_truncation_wedge_5b90b101.py +0 -0
  123. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_e2e/test_workspace_lifecycle.py +0 -0
  124. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_integration/__init__.py +0 -0
  125. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_integration/helpers.py +0 -0
  126. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_integration/test_flagship_loop.py +0 -0
  127. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_integration/test_followup_roundtrips.py +0 -0
  128. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_integration/test_full_workflow.py +0 -0
  129. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_integration/test_prompt_template_contract.py +0 -0
  130. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_integration/test_restore_roundtrips.py +0 -0
  131. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_integration/test_stalled_and_acceptance.py +0 -0
  132. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_states/__init__.py +0 -0
  133. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_states/test_compact_for_llm.py +0 -0
  134. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_states/test_critic_enforcer.py +0 -0
  135. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_states/test_critic_review_cap.py +0 -0
  136. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_states/test_emission_map.py +0 -0
  137. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_states/test_executing_metadata.py +0 -0
  138. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_states/test_executing_prefix_stability.py +0 -0
  139. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_states/test_executing_review_routing.py +0 -0
  140. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_states/test_planning_artifact.py +0 -0
  141. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_states/test_planning_metadata.py +0 -0
  142. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_states/test_selection.py +0 -0
  143. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_states/test_size_compaction_wiring.py +0 -0
  144. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_states/test_stall_recovery.py +0 -0
  145. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_strategies/__init__.py +0 -0
  146. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_strategies/test_identity_block.py +0 -0
  147. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_strategies/test_llm_generate_span.py +0 -0
  148. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_strategies/test_llm_providers.py +0 -0
  149. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_strategies/test_llm_watchdog.py +0 -0
  150. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_strategies/test_prepare_history_contract.py +0 -0
  151. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_strategies/test_qwen_retry.py +0 -0
  152. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_strategies/test_qwen_strategy.py +0 -0
  153. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_strategies/test_qwen_truncation.py +0 -0
  154. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_strategies/test_qwen_workspace_envelope.py +0 -0
  155. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_strategies/test_scripted_llm_contract.py +0 -0
  156. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_strategies/test_telemetry_attributes.py +0 -0
  157. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_strategies/test_turn_budget_block.py +0 -0
  158. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_tools/__init__.py +0 -0
  159. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_tools/test_bundle_and_restore.py +0 -0
  160. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_tools/test_delegation_identity.py +0 -0
  161. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_tools/test_edit_blocks.py +0 -0
  162. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_tools/test_fake_executor.py +0 -0
  163. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_tools/test_handover_sidecars.py +0 -0
  164. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_tools/test_load_handover_resolution.py +0 -0
  165. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_tools/test_output_refs.py +0 -0
  166. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_tools/test_sandbox_import_surface.py +0 -0
  167. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_tools/test_send_actions_error.py +0 -0
  168. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_tools/test_shell_executing_loop.py +0 -0
  169. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_tools/test_shell_tools.py +0 -0
  170. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_tools/test_subagent_handover.py +0 -0
  171. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_tools/test_three_tier_tools.py +0 -0
  172. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_tools/test_workspace_actions.py +0 -0
  173. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tests/test_tools/test_workspace_tools.py +0 -0
  174. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tools/__init__.py +0 -0
  175. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tools/actions/__init__.py +0 -0
  176. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tools/actions/base.py +0 -0
  177. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tools/actions/final_answer.py +0 -0
  178. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tools/actions/load_handover.py +0 -0
  179. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tools/actions/visit_page_action.py +0 -0
  180. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tools/actions/web_search_action.py +0 -0
  181. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tools/actions/wikipedia_search_action.py +0 -0
  182. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tools/actions/workspace.py +0 -0
  183. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tools/base.py +0 -0
  184. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tools/execution.py +0 -0
  185. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tools/functions/__init__.py +0 -0
  186. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tools/functions/base.py +0 -0
  187. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tools/functions/hitl.py +0 -0
  188. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tools/models.py +0 -0
  189. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tools/platform/__init__.py +0 -0
  190. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tools/platform/base.py +0 -0
  191. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tools/platform/edit_blocks.py +0 -0
  192. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tools/platform/files_ops.py +0 -0
  193. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tools/platform/shell.py +0 -0
  194. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tools/platform/web_search.py +0 -0
  195. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tools/platform/workspace_tools.py +0 -0
  196. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/tools/routing.py +0 -0
  197. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/trace.py +0 -0
  198. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/agent/utils.py +0 -0
  199. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/common/__init__.py +0 -0
  200. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/common/codec.py +0 -0
  201. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/common/errors.py +0 -0
  202. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/common/models.py +0 -0
  203. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/common/output_window.py +0 -0
  204. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/common/tests/__init__.py +0 -0
  205. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/common/tests/fake_vm.py +0 -0
  206. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/common/tests/fixtures.py +0 -0
  207. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/common/tests/test_codec_channel.py +0 -0
  208. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/common/tests/test_output_window.py +0 -0
  209. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/common/tests/utils.py +0 -0
  210. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/logging/__init__.py +0 -0
  211. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/logging/formatters.py +0 -0
  212. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate/logging/processors.py +0 -0
  213. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate_agent.egg-info/dependency_links.txt +0 -0
  214. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate_agent.egg-info/entry_points.txt +0 -0
  215. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/src/aisolate_agent.egg-info/requires.txt +0 -0
  216. {aisolate_agent-0.3.4 → aisolate_agent-0.3.6}/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.4
3
+ Version: 0.3.6
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.4-py3-none-any.whl
33
+ pip install path/to/aisolate_agent-0.3.6-py3-none-any.whl
34
34
  ```
35
35
 
36
36
  ## Usage
@@ -5,7 +5,7 @@ AI Agent with intelligent planning and reasoning
5
5
  ## Local Installation
6
6
 
7
7
  ```bash
8
- pip install path/to/aisolate_agent-0.3.4-py3-none-any.whl
8
+ pip install path/to/aisolate_agent-0.3.6-py3-none-any.whl
9
9
  ```
10
10
 
11
11
  ## Usage
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "aisolate-agent"
7
- version = "0.3.4"
7
+ version = "0.3.6"
8
8
  description = "AI Agent with intelligent planning and reasoning"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -1501,6 +1501,9 @@ class Agent:
1501
1501
  context.spine_compactor = SizeCompactor.from_settings(
1502
1502
  self.settings
1503
1503
  )
1504
+ context.max_consecutive_failures = (
1505
+ self.settings.max_consecutive_failures
1506
+ )
1504
1507
  if context.remote_python_executor is not None:
1505
1508
  context.remote_python_executor.output_window = (
1506
1509
  self.settings.output_window_chars
@@ -18,6 +18,14 @@ class AgentSettings(BaseSettings):
18
18
  default=3,
19
19
  description="Maximum number of recovery attempts for failed operations",
20
20
  )
21
+ max_consecutive_failures: int = Field(
22
+ default=5,
23
+ description=(
24
+ "Consecutive failed tool executions (regardless of error text) "
25
+ "tolerated before the stall ladder intervenes; 0 disables the "
26
+ "failure signal"
27
+ ),
28
+ )
21
29
 
22
30
  debug_mode: bool = Field(
23
31
  default=False, description="Enable debug logging and additional validation"
@@ -333,6 +333,11 @@ class AgentContext:
333
333
  # first turn's reported usage.
334
334
  self.spine_compactor: Optional[Any] = None
335
335
 
336
+ # Consecutive-failure threshold for the executing ledger, stamped
337
+ # from AgentSettings by the run owner alongside spine_compactor.
338
+ # None = the ledger's own default; 0 disables the signal.
339
+ self.max_consecutive_failures: Optional[int] = None
340
+
336
341
  # The user's locale (AgentSettings.locale), stamped by the run owner
337
342
  # like workspace_root_display; the identity block renders it so
338
343
  # number/date/time formatting in user-facing output follows the
@@ -184,6 +184,13 @@ def build_summary(context: "AgentContext", dropped: Sequence[Event]) -> str:
184
184
  "disk (re-read it when needed), and your checklist, memory, and "
185
185
  "workspace listing render below as always."
186
186
  ]
187
+ if getattr(context, "remote_python_executor", None) is not None:
188
+ lines.append(
189
+ "The source of the condensed cells is still in the kernel: "
190
+ "successful cells are collected in .aisolate/cells_ok.py, and "
191
+ "%history / In[n] hold the full input history — recover code "
192
+ "from there instead of rewriting it from memory."
193
+ )
187
194
 
188
195
  finished = [
189
196
  rec
@@ -2,6 +2,7 @@
2
2
 
3
3
  import json
4
4
  import os
5
+ import re
5
6
  from typing import Any, Dict, List, Optional, Set, Tuple, TYPE_CHECKING
6
7
 
7
8
  from openai_harmony import Role
@@ -84,10 +85,12 @@ class ProgressLedger:
84
85
  """One progress ledger per executing run — two signals, one owner.
85
86
 
86
87
  Signal 1 (terminal): the exact no-progress count. A turn with no action,
87
- or a byte-identical repeated observation, increments it; a novel
88
- observation or a drafted answer resets it; at ``MAX_NO_PROGRESS`` the run
89
- stalls. This is the only signal allowed to end a run — it proves zero
90
- information gain.
88
+ or a repeated observation, increments it; a novel observation or a
89
+ drafted answer resets it; at ``MAX_NO_PROGRESS`` the run stalls. This is
90
+ the only signal allowed to end a run — it proves zero information gain.
91
+ Repeat identity ignores nondeterministic tokens (UUIDs, long hex ids,
92
+ memory addresses): a retried upload whose object name embeds a fresh
93
+ ``uuid4().hex`` per attempt is the same failure, not new information.
91
94
 
92
95
  Signal 2 (advisory): the checklist stall clock. Every executing turn
93
96
  observes the ``task.md`` fingerprint; once it has not advanced for
@@ -96,22 +99,48 @@ class ProgressLedger:
96
99
  ``CHECKLIST_REMINDER_EVERY`` further stalled turns. It never terminates
97
100
  the run — budget exhaustion is an honest, recoverable outcome, and a
98
101
  coarse heuristic must not kill legitimately long single-item work.
102
+
103
+ Signal 3 (terminal, shares the ladder): consecutive failed executions.
104
+ Repeat identity misses a wedge whose error text genuinely shifts between
105
+ attempts; a run that cannot execute ANYTHING successfully for
106
+ ``max_consecutive_failures`` turns is wedged regardless. One success
107
+ resets the count. The threshold is settable per run
108
+ (``AgentSettings.max_consecutive_failures``); 0 disables.
99
109
  """
100
110
 
101
111
  #: Consecutive no-progress turns tolerated before the run stalls.
102
112
  MAX_NO_PROGRESS = 5
103
113
 
114
+ #: Default consecutive failed executions tolerated before escalation.
115
+ MAX_CONSECUTIVE_FAILURES = 5
116
+
104
117
  #: Executing turns without checklist advance before the notice fires.
105
118
  CHECKLIST_STALL_TURNS = 8
106
119
 
107
120
  #: Further stalled turns between notice repetitions.
108
121
  CHECKLIST_REMINDER_EVERY = 4
109
122
 
123
+ #: Nondeterministic tokens scrubbed before repeat comparison: memory
124
+ #: addresses, dashed UUIDs, and long hex runs (``uuid4().hex`` object
125
+ #: names, digests). The hex rule requires at least one a-f letter so
126
+ #: plain large numbers (real data) never collapse into a false repeat.
127
+ _NONDETERMINISTIC = re.compile(
128
+ r"0x[0-9a-fA-F]{4,}"
129
+ r"|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
130
+ r"|(?=[0-9]*[a-f])[0-9a-f]{16,}"
131
+ )
132
+
110
133
  def __init__(self) -> None:
111
134
  self.no_progress_count: int = 0
112
135
  self._seen_results: Set[str] = set()
113
136
  self._checklist_fingerprint: Optional[str] = None
114
137
  self._stall_turns: int = 0
138
+ self.max_consecutive_failures: int = self.MAX_CONSECUTIVE_FAILURES
139
+ self.consecutive_failures: int = 0
140
+
141
+ @classmethod
142
+ def _fingerprint(cls, observation: str) -> str:
143
+ return cls._NONDETERMINISTIC.sub("•", observation)
115
144
 
116
145
  # -- signal 2: checklist stall clock ---------------------------------
117
146
 
@@ -159,11 +188,12 @@ class ProgressLedger:
159
188
  # -- signal 1: exact no-progress count --------------------------------
160
189
 
161
190
  def observe_result(self, compacted: str) -> bool:
162
- """Account one observation; ``True`` for a byte-identical repeat."""
163
- if compacted in self._seen_results:
191
+ """Account one observation; ``True`` for a repeat (up to random ids)."""
192
+ fingerprint = self._fingerprint(compacted)
193
+ if fingerprint in self._seen_results:
164
194
  self.no_progress_count += 1
165
195
  return True
166
- self._seen_results.add(compacted)
196
+ self._seen_results.add(fingerprint)
167
197
  self.no_progress_count = 0
168
198
  return False
169
199
 
@@ -176,8 +206,38 @@ class ProgressLedger:
176
206
  self.no_progress_count = 0
177
207
 
178
208
  def reset_counter(self) -> None:
179
- """Start a fresh no-progress window (stall recovery granted)."""
209
+ """Start a fresh window on both terminal signals (recovery granted)."""
180
210
  self.no_progress_count = 0
211
+ self.consecutive_failures = 0
212
+
213
+ # -- signal 3: consecutive failed executions ---------------------------
214
+
215
+ def observe_failure(self, ok: bool) -> None:
216
+ """Account one execution outcome; a success resets the streak."""
217
+ self.consecutive_failures = 0 if ok else self.consecutive_failures + 1
218
+
219
+ @property
220
+ def failure_stalled(self) -> bool:
221
+ return (
222
+ self.max_consecutive_failures > 0
223
+ and self.consecutive_failures >= self.max_consecutive_failures
224
+ )
225
+
226
+ def failure_notice(self) -> Optional[str]:
227
+ """In-band warning on the two failures before the threshold."""
228
+ if self.max_consecutive_failures <= 0:
229
+ return None
230
+ remaining = self.max_consecutive_failures - self.consecutive_failures
231
+ if remaining <= 0 or remaining > 2:
232
+ return None
233
+ return (
234
+ f"\n[framework notice] {self.consecutive_failures} consecutive "
235
+ "executions have failed. If the blocker is external (a service "
236
+ "down, missing data), stop retrying — record it and work on "
237
+ "something else or deliver with the gap named. After "
238
+ f"{remaining} more consecutive failure(s) the framework "
239
+ "intervenes."
240
+ )
181
241
 
182
242
  def clear_seen(self) -> None:
183
243
  """Forget seen observations (a re-planned cycle starts clean)."""
@@ -190,8 +250,9 @@ class ProgressLedger:
190
250
  def repeat_notice(self, remaining: int) -> str:
191
251
  """The in-band first response to a repeated observation."""
192
252
  return (
193
- "\n[framework notice] This result is byte-identical to an "
194
- "earlier one in this run — the call added no new information. "
253
+ "\n[framework notice] This result is identical to an earlier "
254
+ "one in this run (ignoring random identifiers) — the call added "
255
+ "no new information. "
195
256
  f"After {remaining} more repeated result(s) the framework "
196
257
  "intervenes and will eventually end the run. If the task is "
197
258
  "complete, deliver NOW by calling final_answer(...); otherwise "
@@ -281,6 +342,10 @@ class ExecutingState(AbstractState):
281
342
  context.set_state_hint("tool_choice", "required")
282
343
  context.set_state_hint("reasoning_effort", "medium")
283
344
 
345
+ limit = getattr(context, "max_consecutive_failures", None)
346
+ if limit is not None:
347
+ self._ledger.max_consecutive_failures = limit
348
+
284
349
  # Remove HITL tools — clarification phase is over, the agent must
285
350
  # execute autonomously. Keeping ask_user available tempts the LLM
286
351
  # into asking trivial preference questions mid-execution; there is
@@ -503,12 +568,18 @@ class ExecutingState(AbstractState):
503
568
  truncated = len(compacted) < len(output)
504
569
 
505
570
  # Progress accounting on the STORED result bytes (the pre-annotation
506
- # compacted form): a byte-identical repeat added zero new information
507
- # to the trace. The first response is in-band — the model is told, in
508
- # the observation itself, that it is repeating and how to deliver.
571
+ # compacted form, normalized for random ids): a repeat added zero new
572
+ # information to the trace. The first response is in-band — the model
573
+ # is told, in the observation itself, that it is repeating and how to
574
+ # deliver.
509
575
  # Only a model that ignores the notice until the threshold stalls the
510
576
  # run; the crossing repeat emits nothing, it had nothing to add.
511
- if self._ledger.observe_result(compacted):
577
+ # Both terminal signals observe every result; at most ONE escalates a
578
+ # turn (a repeated failure is also a failure — climbing both ladders
579
+ # at once would skip rungs).
580
+ repeated_observation = self._ledger.observe_result(compacted)
581
+ self._ledger.observe_failure(ok)
582
+ if repeated_observation:
512
583
  logger.warning(
513
584
  "Repeated observation adds no new information",
514
585
  state="executing",
@@ -528,6 +599,25 @@ class ExecutingState(AbstractState):
528
599
  - self._ledger.no_progress_count
529
600
  )
530
601
  compacted += self._ledger.repeat_notice(remaining)
602
+ elif self._ledger.failure_stalled:
603
+ logger.warning(
604
+ "Consecutive execution failures crossed the threshold",
605
+ state="executing",
606
+ consecutive_failures=self._ledger.consecutive_failures,
607
+ )
608
+ stalled = self._escalate_stall(
609
+ context, repeated=tool_name, reason="failures"
610
+ )
611
+ if stalled is not None:
612
+ return stalled
613
+ compacted += (
614
+ "\n[framework notice] Course change required — follow "
615
+ "the steering instruction below."
616
+ )
617
+ else:
618
+ failure_notice = self._ledger.failure_notice()
619
+ if failure_notice is not None:
620
+ compacted += failure_notice
531
621
 
532
622
  stall_notice = self._ledger.checklist_stall_notice()
533
623
  if stall_notice is not None:
@@ -549,30 +639,48 @@ class ExecutingState(AbstractState):
549
639
  )
550
640
 
551
641
  def _escalate_stall(
552
- self, context: "AgentContext", repeated: str
642
+ self, context: "AgentContext", repeated: str, reason: str = "repeat"
553
643
  ) -> Optional[Stalled]:
554
644
  """Advance the stall ladder one rung; ``None`` grants a recovery.
555
645
 
646
+ Both terminal signals share the ONE ladder position
647
+ (``context.stall_recoveries``); ``reason`` selects the steering text
648
+ ("repeat" = identical observations, "failures" = consecutive failed
649
+ executions).
650
+
556
651
  Rung 1: steering + fresh no-progress window, the loop continues.
557
652
  Rung 2: re-plan steering, ``Stalled`` routes to planning.
558
653
  Rung 3: ``Stalled`` routes to finished — the run ends, loudly.
559
654
  """
560
655
  context.stall_recoveries += 1
561
656
  rung = context.stall_recoveries
562
- count = self._ledger.no_progress_count
657
+ count = (
658
+ self._ledger.consecutive_failures
659
+ if reason == "failures"
660
+ else self._ledger.no_progress_count
661
+ )
563
662
  logger.warning(
564
663
  "Stall threshold crossed",
565
664
  state="executing",
566
665
  escalation=rung,
567
- no_progress_count=count,
666
+ reason=reason,
667
+ count=count,
568
668
  repeated=repeated,
569
669
  )
570
670
  if rung == 1:
571
- self._pending_steering = self._recovery_steering(repeated, count)
671
+ self._pending_steering = (
672
+ self._failure_recovery_steering(repeated, count)
673
+ if reason == "failures"
674
+ else self._recovery_steering(repeated, count)
675
+ )
572
676
  self._ledger.reset_counter()
573
677
  return None
574
678
  if rung == 2:
575
- self._pending_steering = self._replan_steering(repeated)
679
+ self._pending_steering = (
680
+ self._failure_replan_steering(repeated, count)
681
+ if reason == "failures"
682
+ else self._replan_steering(repeated)
683
+ )
576
684
  self._ledger.reset_counter()
577
685
  self._ledger.clear_seen()
578
686
  return Stalled(text="", no_progress_count=count)
@@ -593,6 +701,34 @@ class ExecutingState(AbstractState):
593
701
  "partial results, naming exactly what is missing and why."
594
702
  )
595
703
 
704
+ @staticmethod
705
+ def _failure_recovery_steering(tool: str, count: int) -> str:
706
+ return (
707
+ "[framework steering — repeated failures] The last "
708
+ f"{count} '{tool}' executions ALL failed. Whatever you are "
709
+ "retrying is not going to succeed by repetition. Decide now:\n"
710
+ "1. If the blocker is external (a service or endpoint down, "
711
+ "missing data), STOP calling it — record the blocker "
712
+ "(write_memory) and continue with work that does not need it.\n"
713
+ "2. If your code is failing, step back: print the intermediate "
714
+ "state, simplify to a minimal version, and rebuild from what "
715
+ "works.\n"
716
+ "3. If nothing can proceed, call final_answer with your best "
717
+ "partial results, naming exactly what is blocked and why."
718
+ )
719
+
720
+ @staticmethod
721
+ def _failure_replan_steering(tool: str, count: int) -> str:
722
+ return (
723
+ "[framework steering — approach failed] Executions kept failing "
724
+ f"(last {count} '{tool}' calls) despite a corrective "
725
+ "instruction: the current plan's approach is not working. "
726
+ "Re-plan now. In the revised plan: treat the failing "
727
+ "step/service as unavailable and route around it; reuse the "
728
+ "workspace results that already exist instead of redoing "
729
+ "completed work."
730
+ )
731
+
596
732
  @staticmethod
597
733
  def _replan_steering(repeated: str) -> str:
598
734
  return (
@@ -222,6 +222,20 @@ def test_summary_carries_registry_and_failure_lines():
222
222
  assert "Boom: file not found" in summary
223
223
 
224
224
 
225
+ def test_summary_points_at_kernel_history_on_the_jupyter_lane():
226
+ log = _spine_log(2)
227
+ dropped = select_settled_range(
228
+ log.events(), keep_recent_pairs=0, target_tokens=10_000
229
+ )
230
+ jupyter_ctx = SimpleNamespace(conversation=log, remote_python_executor=object())
231
+ summary = build_summary(jupyter_ctx, dropped)
232
+ assert ".aisolate/cells_ok.py" in summary
233
+ assert "%history" in summary
234
+
235
+ shell_ctx = SimpleNamespace(conversation=log)
236
+ assert "%history" not in build_summary(shell_ctx, dropped)
237
+
238
+
225
239
  def test_squash_event_shape_matches_the_view_contracts():
226
240
  """kernel author + ok:True + no tool_call_id: passes paired_tools_only,
227
241
  passes ok_code_only (the acceptance view), renders as a TOOL turn."""
@@ -0,0 +1,30 @@
1
+ """Package-boundary checks for the independently released agent."""
2
+
3
+ import subprocess
4
+ import sys
5
+ import textwrap
6
+
7
+
8
+ def test_agent_imports_without_sandbox_package():
9
+ script = textwrap.dedent(
10
+ """
11
+ import sys
12
+
13
+ class SandboxBlocker:
14
+ def find_spec(self, name, path=None, target=None):
15
+ if name == "aisolate.sandbox" or name.startswith("aisolate.sandbox."):
16
+ raise ModuleNotFoundError("aisolate.sandbox is not part of aisolate-agent")
17
+
18
+ sys.meta_path.insert(0, SandboxBlocker())
19
+
20
+ import aisolate.agent
21
+ """
22
+ )
23
+ process = subprocess.run(
24
+ [sys.executable, "-c", script],
25
+ capture_output=True,
26
+ text=True,
27
+ timeout=120,
28
+ check=False,
29
+ )
30
+ assert process.returncode == 0, process.stderr