ouroboros-ai 0.1.1__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ouroboros-ai might be problematic. Click here for more details.
- ouroboros_ai-0.2.0/CHANGELOG.md +86 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/PKG-INFO +32 -2
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/README.md +31 -1
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/pyproject.toml +1 -1
- ouroboros_ai-0.2.0/src/ouroboros/__init__.py +28 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/bigbang/ambiguity.py +109 -48
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/bigbang/interview.py +9 -4
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/cli/commands/run.py +9 -1
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/core/__init__.py +11 -0
- ouroboros_ai-0.2.0/src/ouroboros/core/security.py +327 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/observability/logging.py +70 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/providers/litellm_adapter.py +15 -1
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/bigbang/test_ambiguity.py +148 -11
- ouroboros_ai-0.2.0/tests/unit/core/test_security.py +276 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/observability/test_logging.py +74 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/test_main_entry_point.py +1 -1
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/uv.lock +1 -1
- ouroboros_ai-0.1.1/src/ouroboros/__init__.py +0 -15
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/.gitignore +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/.pre-commit-config.yaml +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/.python-version +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/LICENSE +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/PR-43-CODE-REVIEW-REPORT.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/github-issue-mapping.yaml +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/sprint-status.yaml +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/0-1-project-initialization-with-uv.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/0-2-core-types-and-error-handling.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/0-3-event-store-with-sqlalchemy-core.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/0-4-configuration-and-credentials-management.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/0-5-llm-provider-adapter-with-litellm.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/0-6-cli-skeleton-with-typer-and-rich.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/0-7-structured-logging-with-structlog.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/0-8-checkpoint-and-recovery-system.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/0-9-context-compression-engine.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/1-1-interview-protocol-engine.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/1-2-ambiguity-score-calculation.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/1-3-immutable-seed-generation.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/2-1-three-tier-model-configuration.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/2-2-complexity-based-routing.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/2-3-escalation-on-failure.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/2-4-downgrade-on-success.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/3-1-double-diamond-cycle-implementation.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/3-2-hierarchical-ac-decomposition.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/3-3-atomicity-detection.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/3-4-subagent-isolation.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/4-1-stagnation-detection-4-patterns.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/4-2-lateral-thinking-personas.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/4-3-persona-rotation-strategy.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/5-1-stage-1-mechanical-verification.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/5-2-stage-2-semantic-evaluation.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/5-3-stage-3-multi-model-consensus.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/5-4-consensus-trigger-matrix.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/6-1-drift-measurement-engine.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/6-2-automatic-retrospective.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/7-1-todo-registry.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/implementation-artifacts/stories/7-2-secondary-loop-batch-processing.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/planning-artifacts/architecture.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/planning-artifacts/bmm-workflow-status.yaml +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/planning-artifacts/epics.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/_bmad-output/update-stories.sh +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/docs/running-with-claude-code.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/project-context.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/requirement/1_EXECUTIVE_SUMMARY.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/requirement/2_FULL_SPECIFICATION.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/requirement/3_CONFIG_TEMPLATE.yaml +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/requirement/4_REDDIT_EXAMPLE.md +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/__main__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/bigbang/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/bigbang/seed_generator.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/cli/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/cli/commands/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/cli/commands/config.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/cli/commands/init.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/cli/commands/status.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/cli/formatters/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/cli/formatters/panels.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/cli/formatters/progress.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/cli/formatters/tables.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/cli/main.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/config/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/config/loader.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/config/models.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/core/ac_tree.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/core/context.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/core/errors.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/core/seed.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/core/types.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/evaluation/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/evaluation/consensus.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/evaluation/mechanical.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/evaluation/models.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/evaluation/pipeline.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/evaluation/semantic.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/evaluation/trigger.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/events/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/events/base.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/events/decomposition.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/events/evaluation.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/execution/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/execution/atomicity.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/execution/decomposition.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/execution/double_diamond.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/execution/subagent.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/observability/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/observability/drift.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/observability/retrospective.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/orchestrator/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/orchestrator/adapter.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/orchestrator/events.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/orchestrator/runner.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/orchestrator/session.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/persistence/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/persistence/checkpoint.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/persistence/event_store.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/persistence/migrations/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/persistence/migrations/runner.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/persistence/migrations/scripts/001_initial.sql +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/persistence/schema.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/persistence/uow.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/providers/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/providers/base.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/providers/claude_code_adapter.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/py.typed +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/resilience/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/resilience/lateral.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/resilience/stagnation.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/routing/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/routing/complexity.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/routing/downgrade.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/routing/escalation.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/routing/router.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/routing/tiers.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/secondary/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/secondary/scheduler.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/src/ouroboros/secondary/todo_registry.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/conftest.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/integration/test_entry_point.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/bigbang/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/bigbang/test_interview.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/bigbang/test_seed_generator.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/cli/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/cli/formatters/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/cli/formatters/test_console.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/cli/formatters/test_panels.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/cli/formatters/test_progress.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/cli/formatters/test_tables.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/cli/test_main.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/config/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/config/test_loader.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/config/test_models.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/core/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/core/test_ac_tree.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/core/test_context.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/core/test_errors.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/core/test_seed.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/core/test_types.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/evaluation/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/evaluation/test_consensus.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/evaluation/test_mechanical.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/evaluation/test_models.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/evaluation/test_semantic.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/evaluation/test_trigger.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/events/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/events/test_base.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/events/test_decomposition_events.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/execution/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/execution/test_atomicity.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/execution/test_decomposition.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/execution/test_double_diamond.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/execution/test_subagent_isolation.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/observability/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/observability/test_drift.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/observability/test_retrospective.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/orchestrator/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/orchestrator/test_adapter.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/orchestrator/test_events.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/orchestrator/test_runner.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/orchestrator/test_session.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/persistence/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/persistence/test_checkpoint.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/persistence/test_event_store.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/persistence/test_schema.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/persistence/test_uow.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/providers/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/providers/test_base.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/providers/test_litellm_adapter.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/resilience/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/resilience/test_lateral.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/resilience/test_stagnation.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/routing/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/routing/test_complexity.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/routing/test_downgrade.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/routing/test_escalation.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/routing/test_router.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/routing/test_tiers.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/secondary/__init__.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/secondary/test_scheduler.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/secondary/test_todo_registry.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/test_dependencies_configured.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/test_module_structure.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/test_project_initialization.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tests/unit/test_tooling_configuration.py +0 -0
- {ouroboros_ai-0.1.1 → ouroboros_ai-0.2.0}/tools/sync_github_project.py +0 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.2.0] - 2026-01-27
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
#### Security Module (`ouroboros.core.security`)
|
|
13
|
+
- New security utilities module with comprehensive protection features
|
|
14
|
+
- **API Key Management**
|
|
15
|
+
- `mask_api_key()` - Safely mask API keys for logging (shows only last 4 chars)
|
|
16
|
+
- `validate_api_key_format()` - Basic format validation for API keys
|
|
17
|
+
- **Sensitive Data Detection**
|
|
18
|
+
- `is_sensitive_field()` - Detect sensitive field names (api_key, password, token, etc.)
|
|
19
|
+
- `is_sensitive_value()` - Detect values that look like secrets
|
|
20
|
+
- `mask_sensitive_value()` - Mask potentially sensitive values
|
|
21
|
+
- `sanitize_for_logging()` - Create sanitized copies of dicts for safe logging
|
|
22
|
+
- **Input Validation**
|
|
23
|
+
- `InputValidator` class with size limits for DoS prevention:
|
|
24
|
+
- `MAX_INITIAL_CONTEXT_LENGTH` = 50KB
|
|
25
|
+
- `MAX_USER_RESPONSE_LENGTH` = 10KB
|
|
26
|
+
- `MAX_SEED_FILE_SIZE` = 1MB
|
|
27
|
+
- `MAX_LLM_RESPONSE_LENGTH` = 100KB
|
|
28
|
+
|
|
29
|
+
#### Logging Security
|
|
30
|
+
- Automatic sensitive data masking in structlog processor chain
|
|
31
|
+
- API keys, passwords, tokens are now automatically redacted in all log outputs
|
|
32
|
+
- Nested dictionaries are recursively sanitized
|
|
33
|
+
- Pattern-based detection for values starting with `sk-`, `pk-`, `Bearer`, etc.
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
#### Interview Engine
|
|
38
|
+
- Input validation now uses `InputValidator` for consistent size limits
|
|
39
|
+
- `start_interview()` validates initial context length
|
|
40
|
+
- `record_response()` validates user response length
|
|
41
|
+
|
|
42
|
+
#### LiteLLM Adapter
|
|
43
|
+
- LLM responses are now validated and truncated if exceeding size limits
|
|
44
|
+
- Warning logged when response truncation occurs
|
|
45
|
+
|
|
46
|
+
#### CLI Run Command
|
|
47
|
+
- Seed file size is now validated before loading
|
|
48
|
+
- Protection against oversized seed files
|
|
49
|
+
|
|
50
|
+
### Security
|
|
51
|
+
|
|
52
|
+
- **API Key Management**: Keys are masked in logs, showing only provider prefix and last 4 characters
|
|
53
|
+
- **Input Validation**: All external inputs have size limits to prevent DoS attacks
|
|
54
|
+
- **Log Sanitization**: Sensitive data is automatically masked in all log outputs
|
|
55
|
+
- **Credentials Protection**: `credentials.yaml` continues to use chmod 600 permissions
|
|
56
|
+
|
|
57
|
+
### Tests
|
|
58
|
+
|
|
59
|
+
- Added comprehensive test suite for security module (39 tests)
|
|
60
|
+
- Added sensitive data masking tests for logging module (5 tests)
|
|
61
|
+
- All 1341 tests passing
|
|
62
|
+
|
|
63
|
+
## [0.1.1] - 2026-01-15
|
|
64
|
+
|
|
65
|
+
### Added
|
|
66
|
+
- Initial release with core Ouroboros workflow system
|
|
67
|
+
- Big Bang (Phase 0) - Interview and Seed generation
|
|
68
|
+
- PAL Router (Phase 1) - Progressive Adaptive LLM selection
|
|
69
|
+
- Double Diamond (Phase 2) - Execution engine
|
|
70
|
+
- Resilience (Phase 3) - Stagnation detection and lateral thinking
|
|
71
|
+
- Evaluation (Phase 4) - Mechanical, semantic, and consensus evaluation
|
|
72
|
+
- Secondary Loop (Phase 5) - TODO registry and batch scheduler
|
|
73
|
+
- Orchestrator (Epic 8) - Claude Agent SDK integration
|
|
74
|
+
- CLI interface with Typer
|
|
75
|
+
- Event sourcing with SQLite persistence
|
|
76
|
+
- Structured logging with structlog
|
|
77
|
+
|
|
78
|
+
### Fixed
|
|
79
|
+
- Various bug fixes and stability improvements
|
|
80
|
+
|
|
81
|
+
## [0.1.0] - 2026-01-01
|
|
82
|
+
|
|
83
|
+
### Added
|
|
84
|
+
- Initial project structure
|
|
85
|
+
- Core types and error hierarchy
|
|
86
|
+
- Basic configuration system
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ouroboros-ai
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Self-Improving AI Workflow System
|
|
5
5
|
Author-email: Q00 <jqyu.lee@gmail.com>
|
|
6
6
|
License-File: LICENSE
|
|
@@ -52,7 +52,7 @@ Description-Content-Type: text/markdown
|
|
|
52
52
|
<br/>
|
|
53
53
|
|
|
54
54
|
<p align="center">
|
|
55
|
-
<code>
|
|
55
|
+
<code>74 modules</code> · <code>1,341 tests</code> · <code>97%+ coverage</code>
|
|
56
56
|
</p>
|
|
57
57
|
|
|
58
58
|
<br/>
|
|
@@ -506,6 +506,36 @@ uv run ouroboros status health
|
|
|
506
506
|
|
|
507
507
|
<br/>
|
|
508
508
|
|
|
509
|
+
## ◈ Security
|
|
510
|
+
|
|
511
|
+
<br/>
|
|
512
|
+
|
|
513
|
+
Ouroboros includes built-in security features:
|
|
514
|
+
|
|
515
|
+
| Feature | Description |
|
|
516
|
+
|---------|-------------|
|
|
517
|
+
| **API Key Masking** | Keys are automatically masked in logs (`sk-...xxxx`) |
|
|
518
|
+
| **Log Sanitization** | Sensitive fields (password, token, secret) are redacted |
|
|
519
|
+
| **Input Validation** | Size limits prevent DoS attacks (50KB context, 1MB seed files) |
|
|
520
|
+
| **Credentials Protection** | `credentials.yaml` uses chmod 600 permissions |
|
|
521
|
+
|
|
522
|
+
```python
|
|
523
|
+
from ouroboros.core import mask_api_key, sanitize_for_logging
|
|
524
|
+
|
|
525
|
+
# Mask API keys for display
|
|
526
|
+
masked = mask_api_key("sk-1234567890abcdef") # "sk-...cdef"
|
|
527
|
+
|
|
528
|
+
# Sanitize dicts before logging
|
|
529
|
+
safe_data = sanitize_for_logging({"api_key": "sk-secret", "name": "test"})
|
|
530
|
+
# {"api_key": "<REDACTED>", "name": "test"}
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
<br/>
|
|
534
|
+
|
|
535
|
+
---
|
|
536
|
+
|
|
537
|
+
<br/>
|
|
538
|
+
|
|
509
539
|
## ◈ Development
|
|
510
540
|
|
|
511
541
|
<br/>
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
<br/>
|
|
33
33
|
|
|
34
34
|
<p align="center">
|
|
35
|
-
<code>
|
|
35
|
+
<code>74 modules</code> · <code>1,341 tests</code> · <code>97%+ coverage</code>
|
|
36
36
|
</p>
|
|
37
37
|
|
|
38
38
|
<br/>
|
|
@@ -486,6 +486,36 @@ uv run ouroboros status health
|
|
|
486
486
|
|
|
487
487
|
<br/>
|
|
488
488
|
|
|
489
|
+
## ◈ Security
|
|
490
|
+
|
|
491
|
+
<br/>
|
|
492
|
+
|
|
493
|
+
Ouroboros includes built-in security features:
|
|
494
|
+
|
|
495
|
+
| Feature | Description |
|
|
496
|
+
|---------|-------------|
|
|
497
|
+
| **API Key Masking** | Keys are automatically masked in logs (`sk-...xxxx`) |
|
|
498
|
+
| **Log Sanitization** | Sensitive fields (password, token, secret) are redacted |
|
|
499
|
+
| **Input Validation** | Size limits prevent DoS attacks (50KB context, 1MB seed files) |
|
|
500
|
+
| **Credentials Protection** | `credentials.yaml` uses chmod 600 permissions |
|
|
501
|
+
|
|
502
|
+
```python
|
|
503
|
+
from ouroboros.core import mask_api_key, sanitize_for_logging
|
|
504
|
+
|
|
505
|
+
# Mask API keys for display
|
|
506
|
+
masked = mask_api_key("sk-1234567890abcdef") # "sk-...cdef"
|
|
507
|
+
|
|
508
|
+
# Sanitize dicts before logging
|
|
509
|
+
safe_data = sanitize_for_logging({"api_key": "sk-secret", "name": "test"})
|
|
510
|
+
# {"api_key": "<REDACTED>", "name": "test"}
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
<br/>
|
|
514
|
+
|
|
515
|
+
---
|
|
516
|
+
|
|
517
|
+
<br/>
|
|
518
|
+
|
|
489
519
|
## ◈ Development
|
|
490
520
|
|
|
491
521
|
<br/>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Ouroboros - Self-Improving AI Workflow System.
|
|
2
|
+
|
|
3
|
+
A workflow system that uses Socratic questioning and ontological analysis
|
|
4
|
+
to transform ambiguous requirements into executable specifications.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
# Using CLI
|
|
8
|
+
ouroboros init start "I want to build a task management CLI"
|
|
9
|
+
ouroboros run workflow seed.yaml
|
|
10
|
+
|
|
11
|
+
# Using Python
|
|
12
|
+
from ouroboros.core import Result, ValidationError
|
|
13
|
+
from ouroboros.bigbang import InterviewEngine
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
__version__ = "0.2.0"
|
|
17
|
+
|
|
18
|
+
__all__ = ["__version__", "main"]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def main() -> None:
|
|
22
|
+
"""Main entry point for the Ouroboros CLI.
|
|
23
|
+
|
|
24
|
+
This function invokes the Typer app from ouroboros.cli.main.
|
|
25
|
+
"""
|
|
26
|
+
from ouroboros.cli.main import app
|
|
27
|
+
|
|
28
|
+
app()
|
|
@@ -36,6 +36,9 @@ DEFAULT_MODEL = "openrouter/google/gemini-2.0-flash-001"
|
|
|
36
36
|
# Temperature for reproducible scoring
|
|
37
37
|
SCORING_TEMPERATURE = 0.1
|
|
38
38
|
|
|
39
|
+
# Maximum token limit to prevent cost explosion
|
|
40
|
+
MAX_TOKEN_LIMIT = 8192
|
|
41
|
+
|
|
39
42
|
|
|
40
43
|
class ComponentScore(BaseModel):
|
|
41
44
|
"""Individual component score with justification.
|
|
@@ -106,6 +109,17 @@ class AmbiguityScorer:
|
|
|
106
109
|
Uses LLM to evaluate clarity of goals, constraints, and success criteria
|
|
107
110
|
from interview conversation, producing reproducible scores.
|
|
108
111
|
|
|
112
|
+
Uses adaptive token allocation: starts with `initial_max_tokens` and
|
|
113
|
+
doubles on truncation up to `MAX_TOKEN_LIMIT`. Retries up to `max_retries`
|
|
114
|
+
times on both provider errors and parse failures.
|
|
115
|
+
|
|
116
|
+
Attributes:
|
|
117
|
+
llm_adapter: The LLM adapter for completions.
|
|
118
|
+
model: Model identifier to use.
|
|
119
|
+
temperature: Temperature for reproducibility (default 0.1).
|
|
120
|
+
initial_max_tokens: Starting token limit (default 2048).
|
|
121
|
+
max_retries: Maximum retry attempts (default 3).
|
|
122
|
+
|
|
109
123
|
Example:
|
|
110
124
|
scorer = AmbiguityScorer(llm_adapter=LiteLLMAdapter())
|
|
111
125
|
|
|
@@ -123,7 +137,8 @@ class AmbiguityScorer:
|
|
|
123
137
|
llm_adapter: LiteLLMAdapter
|
|
124
138
|
model: str = DEFAULT_MODEL
|
|
125
139
|
temperature: float = SCORING_TEMPERATURE
|
|
126
|
-
|
|
140
|
+
initial_max_tokens: int = 2048
|
|
141
|
+
max_retries: int = 3
|
|
127
142
|
|
|
128
143
|
async def score(
|
|
129
144
|
self, state: InterviewState
|
|
@@ -135,6 +150,9 @@ class AmbiguityScorer:
|
|
|
135
150
|
- Constraints (30% weight)
|
|
136
151
|
- Success criteria (30% weight)
|
|
137
152
|
|
|
153
|
+
Uses adaptive token allocation: starts with initial_max_tokens and
|
|
154
|
+
doubles on parse failure, up to max_retries attempts.
|
|
155
|
+
|
|
138
156
|
Args:
|
|
139
157
|
state: The interview state to score.
|
|
140
158
|
|
|
@@ -159,57 +177,98 @@ class AmbiguityScorer:
|
|
|
159
177
|
Message(role=MessageRole.USER, content=user_prompt),
|
|
160
178
|
]
|
|
161
179
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
max_tokens=self.max_tokens,
|
|
166
|
-
)
|
|
167
|
-
|
|
168
|
-
result = await self.llm_adapter.complete(messages, config)
|
|
180
|
+
current_max_tokens = self.initial_max_tokens
|
|
181
|
+
last_error: Exception | ProviderError | None = None
|
|
182
|
+
last_response: str = ""
|
|
169
183
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
184
|
+
for attempt in range(self.max_retries):
|
|
185
|
+
config = CompletionConfig(
|
|
186
|
+
model=self.model,
|
|
187
|
+
temperature=self.temperature,
|
|
188
|
+
max_tokens=current_max_tokens,
|
|
175
189
|
)
|
|
176
|
-
return Result.err(result.error)
|
|
177
|
-
|
|
178
|
-
# Parse the LLM response into scores
|
|
179
|
-
try:
|
|
180
|
-
breakdown = self._parse_scoring_response(result.value.content)
|
|
181
|
-
overall_score = self._calculate_overall_score(breakdown)
|
|
182
190
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
191
|
+
result = await self.llm_adapter.complete(messages, config)
|
|
192
|
+
|
|
193
|
+
# Fix #3: Retry on provider errors (rate limits, transient failures)
|
|
194
|
+
if result.is_err:
|
|
195
|
+
last_error = result.error
|
|
196
|
+
log.warning(
|
|
197
|
+
"ambiguity.scoring.provider_error_retrying",
|
|
198
|
+
interview_id=state.interview_id,
|
|
199
|
+
error=str(result.error),
|
|
200
|
+
attempt=attempt + 1,
|
|
201
|
+
max_retries=self.max_retries,
|
|
202
|
+
)
|
|
203
|
+
continue
|
|
187
204
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
overall_score=
|
|
192
|
-
is_ready_for_seed=ambiguity_score.is_ready_for_seed,
|
|
193
|
-
goal_clarity=breakdown.goal_clarity.clarity_score,
|
|
194
|
-
constraint_clarity=breakdown.constraint_clarity.clarity_score,
|
|
195
|
-
success_criteria_clarity=breakdown.success_criteria_clarity.clarity_score,
|
|
196
|
-
)
|
|
205
|
+
# Parse the LLM response into scores
|
|
206
|
+
try:
|
|
207
|
+
breakdown = self._parse_scoring_response(result.value.content)
|
|
208
|
+
overall_score = self._calculate_overall_score(breakdown)
|
|
197
209
|
|
|
198
|
-
|
|
210
|
+
ambiguity_score = AmbiguityScore(
|
|
211
|
+
overall_score=overall_score,
|
|
212
|
+
breakdown=breakdown,
|
|
213
|
+
)
|
|
199
214
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
details={"response_preview": result.value.content[:200]},
|
|
215
|
+
log.info(
|
|
216
|
+
"ambiguity.scoring.completed",
|
|
217
|
+
interview_id=state.interview_id,
|
|
218
|
+
overall_score=overall_score,
|
|
219
|
+
is_ready_for_seed=ambiguity_score.is_ready_for_seed,
|
|
220
|
+
goal_clarity=breakdown.goal_clarity.clarity_score,
|
|
221
|
+
constraint_clarity=breakdown.constraint_clarity.clarity_score,
|
|
222
|
+
success_criteria_clarity=breakdown.success_criteria_clarity.clarity_score,
|
|
223
|
+
tokens_used=current_max_tokens,
|
|
224
|
+
attempt=attempt + 1,
|
|
211
225
|
)
|
|
226
|
+
|
|
227
|
+
return Result.ok(ambiguity_score)
|
|
228
|
+
|
|
229
|
+
except (ValueError, KeyError) as e:
|
|
230
|
+
last_error = e
|
|
231
|
+
last_response = result.value.content
|
|
232
|
+
|
|
233
|
+
# Fix #2: Only increase tokens if response was truncated
|
|
234
|
+
is_truncated = result.value.finish_reason == "length"
|
|
235
|
+
|
|
236
|
+
if is_truncated:
|
|
237
|
+
# Fix #1: Cap token growth with MAX_TOKEN_LIMIT
|
|
238
|
+
next_tokens = min(current_max_tokens * 2, MAX_TOKEN_LIMIT)
|
|
239
|
+
log.warning(
|
|
240
|
+
"ambiguity.scoring.truncated_retrying",
|
|
241
|
+
interview_id=state.interview_id,
|
|
242
|
+
error=str(e),
|
|
243
|
+
attempt=attempt + 1,
|
|
244
|
+
current_tokens=current_max_tokens,
|
|
245
|
+
next_tokens=next_tokens,
|
|
246
|
+
)
|
|
247
|
+
current_max_tokens = next_tokens
|
|
248
|
+
else:
|
|
249
|
+
# Format error without truncation - retry with same tokens
|
|
250
|
+
log.warning(
|
|
251
|
+
"ambiguity.scoring.format_error_retrying",
|
|
252
|
+
interview_id=state.interview_id,
|
|
253
|
+
error=str(e),
|
|
254
|
+
attempt=attempt + 1,
|
|
255
|
+
finish_reason=result.value.finish_reason,
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
# All retries exhausted
|
|
259
|
+
log.warning(
|
|
260
|
+
"ambiguity.scoring.failed",
|
|
261
|
+
interview_id=state.interview_id,
|
|
262
|
+
error=str(last_error),
|
|
263
|
+
response=last_response[:500] if last_response else None,
|
|
264
|
+
max_retries_exhausted=True,
|
|
265
|
+
)
|
|
266
|
+
return Result.err(
|
|
267
|
+
ProviderError(
|
|
268
|
+
f"Failed to parse scoring response after {self.max_retries} attempts: {last_error}",
|
|
269
|
+
details={"response_preview": last_response[:200] if last_response else None},
|
|
212
270
|
)
|
|
271
|
+
)
|
|
213
272
|
|
|
214
273
|
def _build_interview_context(self, state: InterviewState) -> str:
|
|
215
274
|
"""Build context string from interview state.
|
|
@@ -254,15 +313,17 @@ Evaluate three components:
|
|
|
254
313
|
|
|
255
314
|
For each component, provide:
|
|
256
315
|
- A clarity score between 0.0 (completely unclear) and 1.0 (perfectly clear)
|
|
257
|
-
- A brief justification explaining the score
|
|
316
|
+
- A brief justification (1-2 sentences max) explaining the score
|
|
317
|
+
|
|
318
|
+
IMPORTANT: You MUST provide ALL six fields below. Keep justifications concise.
|
|
258
319
|
|
|
259
320
|
Respond in this exact format:
|
|
260
321
|
GOAL_CLARITY_SCORE: <score>
|
|
261
|
-
GOAL_CLARITY_JUSTIFICATION: <justification>
|
|
322
|
+
GOAL_CLARITY_JUSTIFICATION: <justification in 1-2 sentences>
|
|
262
323
|
CONSTRAINT_CLARITY_SCORE: <score>
|
|
263
|
-
CONSTRAINT_CLARITY_JUSTIFICATION: <justification>
|
|
324
|
+
CONSTRAINT_CLARITY_JUSTIFICATION: <justification in 1-2 sentences>
|
|
264
325
|
SUCCESS_CRITERIA_CLARITY_SCORE: <score>
|
|
265
|
-
SUCCESS_CRITERIA_CLARITY_JUSTIFICATION: <justification>
|
|
326
|
+
SUCCESS_CRITERIA_CLARITY_JUSTIFICATION: <justification in 1-2 sentences>
|
|
266
327
|
|
|
267
328
|
Be strict in your evaluation. Scores above 0.8 require very specific, measurable requirements."""
|
|
268
329
|
|
|
@@ -17,6 +17,7 @@ from pydantic import BaseModel, Field
|
|
|
17
17
|
import structlog
|
|
18
18
|
|
|
19
19
|
from ouroboros.core.errors import ProviderError, ValidationError
|
|
20
|
+
from ouroboros.core.security import InputValidator
|
|
20
21
|
from ouroboros.core.types import Result
|
|
21
22
|
from ouroboros.providers.base import (
|
|
22
23
|
CompletionConfig,
|
|
@@ -186,9 +187,11 @@ class InterviewEngine:
|
|
|
186
187
|
Returns:
|
|
187
188
|
Result containing the new InterviewState or ValidationError.
|
|
188
189
|
"""
|
|
189
|
-
|
|
190
|
+
# Validate initial context with security limits
|
|
191
|
+
is_valid, error_msg = InputValidator.validate_initial_context(initial_context)
|
|
192
|
+
if not is_valid:
|
|
190
193
|
return Result.err(
|
|
191
|
-
ValidationError(
|
|
194
|
+
ValidationError(error_msg, field="initial_context")
|
|
192
195
|
)
|
|
193
196
|
|
|
194
197
|
if interview_id is None:
|
|
@@ -285,9 +288,11 @@ class InterviewEngine:
|
|
|
285
288
|
Returns:
|
|
286
289
|
Result containing updated state or ValidationError.
|
|
287
290
|
"""
|
|
288
|
-
|
|
291
|
+
# Validate user response with security limits
|
|
292
|
+
is_valid, error_msg = InputValidator.validate_user_response(user_response)
|
|
293
|
+
if not is_valid:
|
|
289
294
|
return Result.err(
|
|
290
|
-
ValidationError(
|
|
295
|
+
ValidationError(error_msg, field="user_response")
|
|
291
296
|
)
|
|
292
297
|
|
|
293
298
|
if state.is_complete:
|
|
@@ -15,6 +15,7 @@ import yaml
|
|
|
15
15
|
|
|
16
16
|
from ouroboros.cli.formatters import console
|
|
17
17
|
from ouroboros.cli.formatters.panels import print_error, print_info, print_success
|
|
18
|
+
from ouroboros.core.security import InputValidator
|
|
18
19
|
|
|
19
20
|
app = typer.Typer(
|
|
20
21
|
name="run",
|
|
@@ -33,8 +34,15 @@ def _load_seed_from_yaml(seed_file: Path) -> dict:
|
|
|
33
34
|
Seed configuration dictionary.
|
|
34
35
|
|
|
35
36
|
Raises:
|
|
36
|
-
typer.Exit: If file cannot be loaded.
|
|
37
|
+
typer.Exit: If file cannot be loaded or exceeds size limit.
|
|
37
38
|
"""
|
|
39
|
+
# Security: Validate file size to prevent DoS
|
|
40
|
+
file_size = seed_file.stat().st_size
|
|
41
|
+
is_valid, error_msg = InputValidator.validate_seed_file_size(file_size)
|
|
42
|
+
if not is_valid:
|
|
43
|
+
print_error(f"Seed file validation failed: {error_msg}")
|
|
44
|
+
raise typer.Exit(1)
|
|
45
|
+
|
|
38
46
|
try:
|
|
39
47
|
with open(seed_file) as f:
|
|
40
48
|
return yaml.safe_load(f)
|
|
@@ -27,6 +27,12 @@ from ouroboros.core.seed import (
|
|
|
27
27
|
Seed,
|
|
28
28
|
SeedMetadata,
|
|
29
29
|
)
|
|
30
|
+
from ouroboros.core.security import (
|
|
31
|
+
InputValidator,
|
|
32
|
+
mask_api_key,
|
|
33
|
+
sanitize_for_logging,
|
|
34
|
+
validate_api_key_format,
|
|
35
|
+
)
|
|
30
36
|
from ouroboros.core.types import CostUnits, DriftScore, EventPayload, Result
|
|
31
37
|
|
|
32
38
|
__all__ = [
|
|
@@ -59,4 +65,9 @@ __all__ = [
|
|
|
59
65
|
"compress_context",
|
|
60
66
|
"compress_context_with_llm",
|
|
61
67
|
"create_filtered_context",
|
|
68
|
+
# Security utilities
|
|
69
|
+
"InputValidator",
|
|
70
|
+
"mask_api_key",
|
|
71
|
+
"validate_api_key_format",
|
|
72
|
+
"sanitize_for_logging",
|
|
62
73
|
]
|