devops-bot-sdk 1.6.32__tar.gz → 1.6.34__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.
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/PKG-INFO +1 -1
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/devops_bot_sdk.egg-info/SOURCES.txt +21 -1
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/__init__.py +2 -2
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agentd.py +21 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/client.py +178 -17
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/config.py +96 -0
- devops_bot_sdk-1.6.34/sdk/estimator.py +246 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/formats.py +28 -2
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/graph_recon.py +137 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/graphify.py +309 -21
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/intake.py +154 -9
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ipc/handlers.py +422 -15
- devops_bot_sdk-1.6.34/sdk/journal.py +388 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/local_exec.py +259 -50
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/plan_run.py +26 -4
- devops_bot_sdk-1.6.34/sdk/prd_progress.py +288 -0
- devops_bot_sdk-1.6.34/sdk/providers.py +631 -0
- devops_bot_sdk-1.6.34/sdk/resume.py +383 -0
- devops_bot_sdk-1.6.34/sdk/router.py +420 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/self_report.py +33 -14
- devops_bot_sdk-1.6.34/sdk/task_types.py +199 -0
- devops_bot_sdk-1.6.34/sdk/tiers/__init__.py +17 -0
- devops_bot_sdk-1.6.34/sdk/tiers/mini_agent.py +336 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/api.py +84 -1
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/models.py +15 -1
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/static/app.js +87 -10
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/static/chat.html +27 -8
- devops_bot_sdk-1.6.34/sdk/write_hook.py +242 -0
- devops_bot_sdk-1.6.34/tests/test_cheap_tier.py +428 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_client_jira_projects.py +45 -4
- devops_bot_sdk-1.6.34/tests/test_estimator.py +285 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_formats.py +56 -5
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_graphify_build.py +134 -4
- devops_bot_sdk-1.6.34/tests/test_journal.py +288 -0
- devops_bot_sdk-1.6.34/tests/test_local_exec_provider.py +295 -0
- devops_bot_sdk-1.6.34/tests/test_prd_artifact.py +473 -0
- devops_bot_sdk-1.6.34/tests/test_providers.py +378 -0
- devops_bot_sdk-1.6.34/tests/test_resume.py +495 -0
- devops_bot_sdk-1.6.34/tests/test_router.py +447 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_self_report.py +23 -1
- devops_bot_sdk-1.6.34/tests/test_tier_flag_parity.py +364 -0
- devops_bot_sdk-1.6.34/tests/test_write_hook.py +289 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/README.md +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/devops_bot_sdk.egg-info/requires.txt +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/devops_bot_sdk.egg-info/top_level.txt +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/pyproject.toml +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/__init__.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/activity.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/builder.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/chat.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/connectors.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/engines/__init__.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/engines/claude_code.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/engines/http.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/engines/mcp.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/engines/n8n.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/engines/shell.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/integrations.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/inventory.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/library/__init__.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/manifest.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/prompt_studio.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/prompt_template.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/prompt_vars.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/run_log.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/schedules.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/secrets.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/ticket_run.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/usage.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/varstore.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/browser_profile.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/cli.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/collectors/files.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/collectors/process.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/collectors/screenshot.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/crucial.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/departments.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/design_verify/__init__.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/design_verify/assets.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/design_verify/bootstrap.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/design_verify/browser.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/design_verify/compare.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/design_verify/loop.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/exceptions.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/git_ops.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/github_ingest.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/hooks/__init__.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/hooks/crucial_guard.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ipc/__init__.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ipc/electron_bridge.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/models/__init__.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/models/envelope.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/models/requests.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/models/responses.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/models/snapshots.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/phase_timer.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/platform_compat.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/proof_readers.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/py.typed +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/run_auto.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/scheduler.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/self_report_local.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/sse.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/task_file.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/test.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/test_pipeline.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ticket_fields.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/__init__.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/dashboard.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/health.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/ip_allowlist.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/jobs.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/runner.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/security.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/server.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/service.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/session.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/setup_manager.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/static/agents.html +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/static/custom-agents.html +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/static/default-agents.html +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/static/index.html +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/static/login.html +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/static/report.html +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/static/runs.html +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/static/schedules.html +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/static/setup.html +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/static/styles.css +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/static/tickets.html +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/updater.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/url_access.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/vps_health.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/setup.cfg +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_agent_activity.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_agent_activity_tracking.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_agent_chat_features.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_agent_listing.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_agent_prompt_async.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_agent_prompt_models.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_agent_run_ticket.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_agent_schedules.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_agent_workspace_settings.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_agents_connectors_builder.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_allowed_tools_config.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_auto_agent_dispatch.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_browser_profile.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_config_token_diagnostics.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_configure_token_owner.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_console_encoding.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_departments.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_epic_decompose_run.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_epic_inherited_agent_id.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_epic_session_isolation.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_git_action.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_github_ingest.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_graph_recon.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_graphify_hook_install.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_health.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_intake_plan_review.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_otp_login.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_phase_timer.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_plan_new_command.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_plan_run_activity.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_proof_readers.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_recon_session_isolation.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_run_auto_agent_only.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_run_outputs.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_schedule_api.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_scheduler.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_service_token_warning.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_task_file_and_plan_run.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_task_file_format_doc.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_ticket_fields.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_ticket_run_resolve_by_id.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_ticket_speed_fixes.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_update_timer.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_updater_extras.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_url_access.py +0 -0
- {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_vps_health.py +0 -0
|
@@ -14,6 +14,7 @@ sdk/client.py
|
|
|
14
14
|
sdk/config.py
|
|
15
15
|
sdk/crucial.py
|
|
16
16
|
sdk/departments.py
|
|
17
|
+
sdk/estimator.py
|
|
17
18
|
sdk/exceptions.py
|
|
18
19
|
sdk/formats.py
|
|
19
20
|
sdk/git_ops.py
|
|
@@ -21,24 +22,31 @@ sdk/github_ingest.py
|
|
|
21
22
|
sdk/graph_recon.py
|
|
22
23
|
sdk/graphify.py
|
|
23
24
|
sdk/intake.py
|
|
25
|
+
sdk/journal.py
|
|
24
26
|
sdk/local_exec.py
|
|
25
27
|
sdk/phase_timer.py
|
|
26
28
|
sdk/plan_run.py
|
|
27
29
|
sdk/platform_compat.py
|
|
30
|
+
sdk/prd_progress.py
|
|
28
31
|
sdk/proof_readers.py
|
|
32
|
+
sdk/providers.py
|
|
29
33
|
sdk/py.typed
|
|
34
|
+
sdk/resume.py
|
|
35
|
+
sdk/router.py
|
|
30
36
|
sdk/run_auto.py
|
|
31
37
|
sdk/scheduler.py
|
|
32
38
|
sdk/self_report.py
|
|
33
39
|
sdk/self_report_local.py
|
|
34
40
|
sdk/sse.py
|
|
35
41
|
sdk/task_file.py
|
|
42
|
+
sdk/task_types.py
|
|
36
43
|
sdk/test.py
|
|
37
44
|
sdk/test_pipeline.py
|
|
38
45
|
sdk/ticket_fields.py
|
|
39
46
|
sdk/updater.py
|
|
40
47
|
sdk/url_access.py
|
|
41
48
|
sdk/vps_health.py
|
|
49
|
+
sdk/write_hook.py
|
|
42
50
|
sdk/agents/__init__.py
|
|
43
51
|
sdk/agents/activity.py
|
|
44
52
|
sdk/agents/builder.py
|
|
@@ -83,6 +91,8 @@ sdk/models/envelope.py
|
|
|
83
91
|
sdk/models/requests.py
|
|
84
92
|
sdk/models/responses.py
|
|
85
93
|
sdk/models/snapshots.py
|
|
94
|
+
sdk/tiers/__init__.py
|
|
95
|
+
sdk/tiers/mini_agent.py
|
|
86
96
|
sdk/ui/__init__.py
|
|
87
97
|
sdk/ui/api.py
|
|
88
98
|
sdk/ui/dashboard.py
|
|
@@ -122,6 +132,7 @@ tests/test_agents_connectors_builder.py
|
|
|
122
132
|
tests/test_allowed_tools_config.py
|
|
123
133
|
tests/test_auto_agent_dispatch.py
|
|
124
134
|
tests/test_browser_profile.py
|
|
135
|
+
tests/test_cheap_tier.py
|
|
125
136
|
tests/test_client_jira_projects.py
|
|
126
137
|
tests/test_config_token_diagnostics.py
|
|
127
138
|
tests/test_configure_token_owner.py
|
|
@@ -130,6 +141,7 @@ tests/test_departments.py
|
|
|
130
141
|
tests/test_epic_decompose_run.py
|
|
131
142
|
tests/test_epic_inherited_agent_id.py
|
|
132
143
|
tests/test_epic_session_isolation.py
|
|
144
|
+
tests/test_estimator.py
|
|
133
145
|
tests/test_formats.py
|
|
134
146
|
tests/test_git_action.py
|
|
135
147
|
tests/test_github_ingest.py
|
|
@@ -138,12 +150,18 @@ tests/test_graphify_build.py
|
|
|
138
150
|
tests/test_graphify_hook_install.py
|
|
139
151
|
tests/test_health.py
|
|
140
152
|
tests/test_intake_plan_review.py
|
|
153
|
+
tests/test_journal.py
|
|
154
|
+
tests/test_local_exec_provider.py
|
|
141
155
|
tests/test_otp_login.py
|
|
142
156
|
tests/test_phase_timer.py
|
|
143
157
|
tests/test_plan_new_command.py
|
|
144
158
|
tests/test_plan_run_activity.py
|
|
159
|
+
tests/test_prd_artifact.py
|
|
145
160
|
tests/test_proof_readers.py
|
|
161
|
+
tests/test_providers.py
|
|
146
162
|
tests/test_recon_session_isolation.py
|
|
163
|
+
tests/test_resume.py
|
|
164
|
+
tests/test_router.py
|
|
147
165
|
tests/test_run_auto_agent_only.py
|
|
148
166
|
tests/test_run_outputs.py
|
|
149
167
|
tests/test_schedule_api.py
|
|
@@ -155,7 +173,9 @@ tests/test_task_file_format_doc.py
|
|
|
155
173
|
tests/test_ticket_fields.py
|
|
156
174
|
tests/test_ticket_run_resolve_by_id.py
|
|
157
175
|
tests/test_ticket_speed_fixes.py
|
|
176
|
+
tests/test_tier_flag_parity.py
|
|
158
177
|
tests/test_update_timer.py
|
|
159
178
|
tests/test_updater_extras.py
|
|
160
179
|
tests/test_url_access.py
|
|
161
|
-
tests/test_vps_health.py
|
|
180
|
+
tests/test_vps_health.py
|
|
181
|
+
tests/test_write_hook.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""AgentOS Desktop SDK — thin HTTPS/SSE client for the Electron app.
|
|
2
2
|
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.34
|
|
4
4
|
|
|
5
5
|
Public surface:
|
|
6
6
|
BackendClient.from_config() — create client from ~/.agentos/config.toml
|
|
@@ -34,7 +34,7 @@ Rules:
|
|
|
34
34
|
- All data egress through submit_webhook only
|
|
35
35
|
"""
|
|
36
36
|
|
|
37
|
-
__version__ = "1.6.
|
|
37
|
+
__version__ = "1.6.34" # SINGLE SOURCE OF TRUTH — bump on every change; pyproject,
|
|
38
38
|
__author__ = "AgentOS" # sdk.client.SDK_VERSION and the UI server version all read this.
|
|
39
39
|
|
|
40
40
|
from sdk.client import BackendClient
|
|
@@ -429,8 +429,25 @@ async def _dispatch_agent_run(
|
|
|
429
429
|
)
|
|
430
430
|
run_log.log_line(_log_path, f"task_id={task_id}")
|
|
431
431
|
|
|
432
|
+
# A custom-agent run writes real files, so it gets the SAME shared
|
|
433
|
+
# post-write hook the coding path uses: journal (synchronous,
|
|
434
|
+
# survives a kill) → requirement overlay → graph refresh. Without
|
|
435
|
+
# this, a custom-agent run produced no graph update and no journal
|
|
436
|
+
# entry at all, so a tier switch during one had no anchor to resume
|
|
437
|
+
# from — the run type was simply outside the resume triple.
|
|
438
|
+
from sdk import write_hook
|
|
439
|
+
from sdk.agents.engines.claude_code import ensure_agent_workspace
|
|
440
|
+
|
|
441
|
+
_workspace = str(ensure_agent_workspace(manifest.name))
|
|
442
|
+
_record_write = write_hook.make_recorder(
|
|
443
|
+
task_id=task_id or f"agent-{manifest.name}",
|
|
444
|
+
repo_path=_workspace,
|
|
445
|
+
)
|
|
446
|
+
_write_events = write_hook.make_event_hook(_record_write)
|
|
447
|
+
|
|
432
448
|
async def _tee(ev: dict) -> None:
|
|
433
449
|
run_log.log_event(_log_path, ev)
|
|
450
|
+
await _write_events(ev)
|
|
434
451
|
|
|
435
452
|
_started = time.monotonic()
|
|
436
453
|
local_result = await claude_code_engine.run(
|
|
@@ -438,6 +455,10 @@ async def _dispatch_agent_run(
|
|
|
438
455
|
on_event=_tee,
|
|
439
456
|
)
|
|
440
457
|
run_log.finish_run(_log_path, local_result)
|
|
458
|
+
# Backstop for writes the stream did not surface (a resumed turn,
|
|
459
|
+
# an engine that tees events elsewhere). Ordering is lost, but an
|
|
460
|
+
# ordered-but-empty journal is worse than an unordered complete one.
|
|
461
|
+
await write_hook.record_result_files(_record_write, local_result)
|
|
441
462
|
# Local ledger + platform `agent_activities` feed. EVERY trigger path
|
|
442
463
|
# reaches this function (cron schedule, ticket label, on-demand
|
|
443
464
|
# run-on-ticket from the UI / CLI / desktop IPC), so a VPS agent run is
|
|
@@ -197,6 +197,30 @@ _ORCHESTRATE_TIMEOUT = 2700.0 # 45 min — covers approval wait + VPS exec
|
|
|
197
197
|
_AUTO_ORCHESTRATE_TIMEOUT = 2700.0 # 45 min — same budget for multi-task auto runs
|
|
198
198
|
|
|
199
199
|
|
|
200
|
+
def _jira_project_records(payload: dict) -> list[dict]:
|
|
201
|
+
"""Normalise a jira-projects envelope to `list[dict]`.
|
|
202
|
+
|
|
203
|
+
The endpoint returns `data` as a FLAT LIST. Sibling endpoints on the same
|
|
204
|
+
gateway wrap theirs in `data.records` / `data.record`, and this shape is not
|
|
205
|
+
documented, so all three are tolerated rather than guessing one — a wrong
|
|
206
|
+
guess reads as "no projects", which is indistinguishable from a real empty
|
|
207
|
+
result and would silently disable the whole picker.
|
|
208
|
+
"""
|
|
209
|
+
data = payload.get("data")
|
|
210
|
+
if isinstance(data, list):
|
|
211
|
+
return [r for r in data if isinstance(r, dict)]
|
|
212
|
+
if isinstance(data, dict):
|
|
213
|
+
records = data.get("records")
|
|
214
|
+
if isinstance(records, list):
|
|
215
|
+
return [r for r in records if isinstance(r, dict)]
|
|
216
|
+
record = data.get("record")
|
|
217
|
+
if isinstance(record, dict):
|
|
218
|
+
return [record]
|
|
219
|
+
if data.get("id") or data.get("jiraProjectId"):
|
|
220
|
+
return [dict(data)]
|
|
221
|
+
return []
|
|
222
|
+
|
|
223
|
+
|
|
200
224
|
class BackendClient:
|
|
201
225
|
"""Thin HTTPS/SSE client — wraps all backend /api/v1/* endpoints.
|
|
202
226
|
|
|
@@ -1096,6 +1120,108 @@ class BackendClient:
|
|
|
1096
1120
|
logger.warning("client.set_task_status error task=%s err=%s", task_id, exc)
|
|
1097
1121
|
return False
|
|
1098
1122
|
|
|
1123
|
+
# ── Cheap tier (T0) — the backend holds the Azure/GCP credentials ───
|
|
1124
|
+
|
|
1125
|
+
async def llm_complete(
|
|
1126
|
+
self,
|
|
1127
|
+
messages: list[dict],
|
|
1128
|
+
*,
|
|
1129
|
+
tools: list[dict] | None = None,
|
|
1130
|
+
step_type: str = "small_task",
|
|
1131
|
+
task_id: str | None = None,
|
|
1132
|
+
budget_tokens: int | None = None,
|
|
1133
|
+
used_tokens: int = 0,
|
|
1134
|
+
max_tokens: int | None = None,
|
|
1135
|
+
timeout: float = 120.0,
|
|
1136
|
+
attempts: int = 3,
|
|
1137
|
+
) -> dict:
|
|
1138
|
+
"""POST /api/v1/llm/complete — one turn of the cheap-tier tool loop.
|
|
1139
|
+
|
|
1140
|
+
The credential asymmetry is deliberate (plan §6): Azure/GCP keys stay
|
|
1141
|
+
**server-side** because the backend is the client for T0, while the
|
|
1142
|
+
Bedrock/subscription credentials stay desktop-side because the local
|
|
1143
|
+
``claude`` CLI is the client for T1/T2. Each tier's creds live where that
|
|
1144
|
+
tier's client runs, so the desktop never holds an Azure key.
|
|
1145
|
+
|
|
1146
|
+
The budget is enforced **server-side** — the desktop is untrusted for
|
|
1147
|
+
accounting — and a breach comes back as ``409 budget_exhausted``, which
|
|
1148
|
+
the router treats as "escalate to T1", not as an error.
|
|
1149
|
+
|
|
1150
|
+
Returns ``{"ok", "text", "tool_calls", "model", "tokens_in",
|
|
1151
|
+
"tokens_out", "status", "reason"}``. Never raises: an unreachable
|
|
1152
|
+
backend is a *routing signal* (the cheap tier is the LESS available one),
|
|
1153
|
+
so it comes back as ``ok=False`` with ``reason="backend_unreachable"``
|
|
1154
|
+
and the router escalates.
|
|
1155
|
+
|
|
1156
|
+
Retries only what is worth retrying, via the existing
|
|
1157
|
+
``is_retryable_backend_error`` — so a saturated Postgres pool surfaced
|
|
1158
|
+
as a 5xx is retried rather than escalating the whole task off the cheap
|
|
1159
|
+
tier for a transient blip.
|
|
1160
|
+
"""
|
|
1161
|
+
path = "/api/v1/llm/complete"
|
|
1162
|
+
payload: dict[str, Any] = {
|
|
1163
|
+
"messages": messages,
|
|
1164
|
+
"step_type": step_type,
|
|
1165
|
+
}
|
|
1166
|
+
if tools:
|
|
1167
|
+
payload["tools"] = tools
|
|
1168
|
+
if task_id:
|
|
1169
|
+
payload["task_id"] = task_id
|
|
1170
|
+
if budget_tokens is not None:
|
|
1171
|
+
payload["budget_tokens"] = budget_tokens
|
|
1172
|
+
if used_tokens:
|
|
1173
|
+
payload["used_tokens"] = used_tokens
|
|
1174
|
+
if max_tokens is not None:
|
|
1175
|
+
payload["max_tokens"] = max_tokens
|
|
1176
|
+
|
|
1177
|
+
def _fail(reason: str, status: int = 0, detail: str = "") -> dict:
|
|
1178
|
+
return {"ok": False, "text": "", "tool_calls": [], "model": None,
|
|
1179
|
+
"tokens_in": 0, "tokens_out": 0, "status": status,
|
|
1180
|
+
"reason": reason, "detail": detail[:300]}
|
|
1181
|
+
|
|
1182
|
+
last = _fail("backend_unreachable")
|
|
1183
|
+
for attempt in range(max(1, attempts)):
|
|
1184
|
+
try:
|
|
1185
|
+
async with httpx.AsyncClient(timeout=timeout) as client:
|
|
1186
|
+
resp = await client.post(
|
|
1187
|
+
self._url(path), json=payload, headers=self._headers,
|
|
1188
|
+
)
|
|
1189
|
+
except Exception as exc: # noqa: BLE001 — network/DNS/TLS
|
|
1190
|
+
last = _fail("backend_unreachable", detail=str(exc))
|
|
1191
|
+
if attempt + 1 < attempts:
|
|
1192
|
+
await asyncio.sleep(min(2.0 * (attempt + 1), 5.0))
|
|
1193
|
+
continue
|
|
1194
|
+
return last
|
|
1195
|
+
body = resp.text or ""
|
|
1196
|
+
if resp.status_code == 409:
|
|
1197
|
+
# The hard ceiling. One mechanism serves both accounting and the
|
|
1198
|
+
# cap, so there is nothing to reconcile between them.
|
|
1199
|
+
return _fail("budget_exhausted", resp.status_code, body)
|
|
1200
|
+
if resp.status_code >= 400:
|
|
1201
|
+
if is_retryable_backend_error(resp.status_code, body) and attempt + 1 < attempts:
|
|
1202
|
+
last = _fail("retryable", resp.status_code, body)
|
|
1203
|
+
await asyncio.sleep(min(2.0 * (attempt + 1), 5.0))
|
|
1204
|
+
continue
|
|
1205
|
+
reason = ("auth" if resp.status_code == 401
|
|
1206
|
+
else "unavailable" if resp.status_code >= 500
|
|
1207
|
+
else "rejected")
|
|
1208
|
+
return _fail(reason, resp.status_code, body)
|
|
1209
|
+
try:
|
|
1210
|
+
data = resp.json() or {}
|
|
1211
|
+
except Exception: # noqa: BLE001 — a 200 with a non-JSON body
|
|
1212
|
+
return _fail("bad_response", resp.status_code, body)
|
|
1213
|
+
return {
|
|
1214
|
+
"ok": True,
|
|
1215
|
+
"text": str(data.get("text") or ""),
|
|
1216
|
+
"tool_calls": list(data.get("tool_calls") or []),
|
|
1217
|
+
"model": data.get("model"),
|
|
1218
|
+
"tokens_in": int(data.get("tokens_in") or 0),
|
|
1219
|
+
"tokens_out": int(data.get("tokens_out") or 0),
|
|
1220
|
+
"status": resp.status_code,
|
|
1221
|
+
"reason": "",
|
|
1222
|
+
}
|
|
1223
|
+
return last
|
|
1224
|
+
|
|
1099
1225
|
async def update_task_ml(
|
|
1100
1226
|
self, task_id: str, fields: dict, model_used: list[str] | None = None,
|
|
1101
1227
|
) -> bool:
|
|
@@ -1214,15 +1340,32 @@ class BackendClient:
|
|
|
1214
1340
|
return None
|
|
1215
1341
|
|
|
1216
1342
|
async def list_jira_projects(
|
|
1217
|
-
self, pv_id: str, limit: int = 100, offset: int = 0,
|
|
1343
|
+
self, pv_id: str = "", limit: int = 100, offset: int = 0,
|
|
1344
|
+
*, all_: bool = False,
|
|
1218
1345
|
) -> list[dict]:
|
|
1219
|
-
"""GET {BE}/ml-api/jira-projects
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1346
|
+
"""GET {BE}/ml-api/jira-projects — project records.
|
|
1347
|
+
|
|
1348
|
+
Two fixes over the original, both of which made an unfiltered listing
|
|
1349
|
+
impossible:
|
|
1350
|
+
|
|
1351
|
+
1. `pvId` is now OMITTED when blank. It used to be sent unconditionally as
|
|
1352
|
+
`pvId=""`, which scopes the query to nothing, so there was no way to
|
|
1353
|
+
ask "every project I can see". That matters because `pvId` is null on
|
|
1354
|
+
the large majority of live rows (57 projects, 3 with a pvId at time of
|
|
1355
|
+
writing) — a pvId-only client can reach almost none of the boards a
|
|
1356
|
+
person would actually name.
|
|
1357
|
+
2. The envelope is read TOLERANTLY. This endpoint returns `data` as a
|
|
1358
|
+
FLAT LIST; the original read `data.records` only, so an unfiltered call
|
|
1359
|
+
would have returned `[]` even once (1) was fixed. Both shapes are now
|
|
1360
|
+
accepted, plus a bare `data.record`.
|
|
1361
|
+
|
|
1362
|
+
Pass `all_=True` to send the gateway's `all=true` (no pagination) instead
|
|
1363
|
+
of `limit`/`offset`. Match on `key` to resolve an existing project before
|
|
1364
|
+
falling back to `create_jira_project` — `key` is the same string a ticket
|
|
1365
|
+
carries as `projectKey`.
|
|
1366
|
+
|
|
1367
|
+
Returns [] on any failure, logged rather than silent, so a broken gateway
|
|
1368
|
+
cannot read as "there are no projects".
|
|
1226
1369
|
"""
|
|
1227
1370
|
from sdk.config import BE_DEFAULT_URL
|
|
1228
1371
|
|
|
@@ -1233,25 +1376,43 @@ class BackendClient:
|
|
|
1233
1376
|
"developer-name": "null",
|
|
1234
1377
|
"product": "",
|
|
1235
1378
|
}
|
|
1379
|
+
params: dict[str, object] = {"offset": offset}
|
|
1380
|
+
if all_:
|
|
1381
|
+
params["all"] = "true"
|
|
1382
|
+
else:
|
|
1383
|
+
params["limit"] = limit
|
|
1384
|
+
# Only scope by product when there is one — see (1) above.
|
|
1385
|
+
if str(pv_id or "").strip():
|
|
1386
|
+
params["pvId"] = str(pv_id).strip()
|
|
1236
1387
|
try:
|
|
1237
|
-
# All three params are always sent — the wire URL keeps its documented
|
|
1238
|
-
# shape even when the caller has no pvId to scope by.
|
|
1239
1388
|
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
1240
|
-
resp = await client.get(
|
|
1241
|
-
url,
|
|
1242
|
-
params={"limit": limit, "offset": offset, "pvId": str(pv_id or "")},
|
|
1243
|
-
headers=headers,
|
|
1244
|
-
)
|
|
1389
|
+
resp = await client.get(url, params=params, headers=headers)
|
|
1245
1390
|
if resp.status_code >= 400:
|
|
1246
1391
|
logger.warning("client.list_jira_projects failed status=%s body=%s",
|
|
1247
1392
|
resp.status_code, resp.text[:300])
|
|
1248
1393
|
return []
|
|
1249
|
-
|
|
1250
|
-
return records if isinstance(records, list) else []
|
|
1394
|
+
return _jira_project_records(resp.json() or {})
|
|
1251
1395
|
except Exception as exc: # noqa: BLE001
|
|
1252
1396
|
logger.warning("client.list_jira_projects error err=%s", exc)
|
|
1253
1397
|
return []
|
|
1254
1398
|
|
|
1399
|
+
async def find_jira_project_by_key(self, key: str) -> dict | None:
|
|
1400
|
+
"""The project whose `key` matches, case-insensitively, or None.
|
|
1401
|
+
|
|
1402
|
+
The verification step before filing tickets: a key that no longer resolves
|
|
1403
|
+
means the board was renamed or deleted, and creating N tickets against it
|
|
1404
|
+
would fail N times with nothing naming the cause. Matched locally over the
|
|
1405
|
+
full list because the gateway exposes no `?key=` filter.
|
|
1406
|
+
"""
|
|
1407
|
+
wanted = str(key or "").strip().casefold()
|
|
1408
|
+
if not wanted:
|
|
1409
|
+
return None
|
|
1410
|
+
for record in await self.list_jira_projects(all_=True):
|
|
1411
|
+
candidate = record.get("key")
|
|
1412
|
+
if isinstance(candidate, str) and candidate.strip().casefold() == wanted:
|
|
1413
|
+
return record
|
|
1414
|
+
return None
|
|
1415
|
+
|
|
1255
1416
|
async def add_comment(self, task_id: str, body: str) -> bool:
|
|
1256
1417
|
"""POST /tasks-ml/{id}/comments — add a comment to a ticket (best-effort).
|
|
1257
1418
|
|
|
@@ -326,6 +326,102 @@ def runtime_allowed_ips() -> list[str]:
|
|
|
326
326
|
return _parse_ip_list(_load_raw().get("allowed_ips", ""))
|
|
327
327
|
|
|
328
328
|
|
|
329
|
+
# ── Feature flags / tunables ──────────────────────────────────────────
|
|
330
|
+
# Read through the same precedence as every other override here — process env,
|
|
331
|
+
# then ``~/.agentos/.env`` (or a CWD ``.env``), then config.toml, then the
|
|
332
|
+
# built-in default. Read at CALL time, never at import, so a flag flipped on a
|
|
333
|
+
# box applies to the next run without restarting the daemon or the UI server.
|
|
334
|
+
|
|
335
|
+
_TRUTHY = frozenset({"1", "true", "yes", "on", "enabled"})
|
|
336
|
+
_FALSY = frozenset({"0", "false", "no", "off", "disabled"})
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def setting(key: str, default: str = "") -> str:
|
|
340
|
+
"""Raw string value of a setting, or ``default`` when unset everywhere.
|
|
341
|
+
|
|
342
|
+
``key`` is the env-var spelling (``AGENTOS_TIERED_ROUTING``); the config.toml
|
|
343
|
+
lookup uses its lowercase form without the ``AGENTOS_`` prefix
|
|
344
|
+
(``tiered_routing``), matching how ``allowed_tools`` / ``allowed_ips``
|
|
345
|
+
already read.
|
|
346
|
+
"""
|
|
347
|
+
for source in (os.environ.get(key, ""), _dotenv_value(key)):
|
|
348
|
+
value = (source or "").strip()
|
|
349
|
+
if value:
|
|
350
|
+
return value
|
|
351
|
+
toml_key = key[len("AGENTOS_"):].lower() if key.startswith("AGENTOS_") else key.lower()
|
|
352
|
+
try:
|
|
353
|
+
value = str(_load_raw().get(toml_key, "") or "").strip()
|
|
354
|
+
except Exception: # noqa: BLE001 — a config hiccup must never break a run
|
|
355
|
+
value = ""
|
|
356
|
+
return value or default
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def flag(key: str, default: bool = False) -> bool:
|
|
360
|
+
"""A boolean feature flag. Anything unrecognised falls back to ``default``.
|
|
361
|
+
|
|
362
|
+
Every new tiered-routing behaviour is gated on one of these and every one
|
|
363
|
+
defaults to **False**, so a machine with nothing configured behaves exactly
|
|
364
|
+
as it does today. That is the primary acceptance criterion for the feature,
|
|
365
|
+
not a nicety: an unset flag must be indistinguishable from the flag not
|
|
366
|
+
existing.
|
|
367
|
+
"""
|
|
368
|
+
raw = setting(key).lower()
|
|
369
|
+
if raw in _TRUTHY:
|
|
370
|
+
return True
|
|
371
|
+
if raw in _FALSY:
|
|
372
|
+
return False
|
|
373
|
+
return default
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def int_setting(key: str, default: int) -> int:
|
|
377
|
+
"""An integer tunable, tolerant of junk (returns ``default`` on any garbage)."""
|
|
378
|
+
try:
|
|
379
|
+
return int(float(setting(key, str(default))))
|
|
380
|
+
except (TypeError, ValueError):
|
|
381
|
+
return default
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def float_setting(key: str, default: float) -> float:
|
|
385
|
+
"""A float tunable, tolerant of junk."""
|
|
386
|
+
try:
|
|
387
|
+
return float(setting(key, str(default)))
|
|
388
|
+
except (TypeError, ValueError):
|
|
389
|
+
return default
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
# The flag names, in one place so a reader can see the whole opt-in surface.
|
|
393
|
+
#: Master switch for the escalation ladder (sdk/router.py). Off → the ladder is
|
|
394
|
+
#: never consulted and run_claude_local is called exactly as it is today.
|
|
395
|
+
FLAG_TIERED_ROUTING = "AGENTOS_TIERED_ROUTING"
|
|
396
|
+
#: Journal + resume-triple assembly (sdk/journal.py, sdk/resume.py). Independent
|
|
397
|
+
#: of the ladder on purpose: phase 2 proves recovery on the subscription tier
|
|
398
|
+
#: ALONE before any tier switching exists.
|
|
399
|
+
FLAG_JOURNAL = "AGENTOS_JOURNAL"
|
|
400
|
+
#: Write the generated PRD to ``docs/prd/<KEY>.md`` in the repo.
|
|
401
|
+
FLAG_PRD_FILE = "AGENTOS_PRD_FILE"
|
|
402
|
+
#: Supervise ``graphify watch`` instead of relying on the debounce.
|
|
403
|
+
FLAG_GRAPH_WATCH = "AGENTOS_GRAPHIFY_WATCH"
|
|
404
|
+
#: Cheap-tier mini agent (sdk/tiers/). Requires backend reachability.
|
|
405
|
+
FLAG_CHEAP_TIER = "AGENTOS_CHEAP_TIER"
|
|
406
|
+
#: Pass ``--resume`` across a provider change. Unverified; see the plan's §12.
|
|
407
|
+
FLAG_CROSS_PROVIDER_RESUME = "AGENTOS_CROSS_PROVIDER_RESUME"
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def tiered_routing_enabled() -> bool:
|
|
411
|
+
"""True when the escalation ladder may run at all."""
|
|
412
|
+
return flag(FLAG_TIERED_ROUTING)
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def journal_enabled() -> bool:
|
|
416
|
+
"""True when writes are journalled and resume prompts may be assembled.
|
|
417
|
+
|
|
418
|
+
Implied by the ladder: escalating without a journal would lose the task
|
|
419
|
+
context the ladder exists to preserve, so turning the ladder on turns this
|
|
420
|
+
on whether or not it was named.
|
|
421
|
+
"""
|
|
422
|
+
return flag(FLAG_JOURNAL) or tiered_routing_enabled()
|
|
423
|
+
|
|
424
|
+
|
|
329
425
|
def add_allowed_ip(ip: str) -> list[str]:
|
|
330
426
|
"""Whitelist an IP/CIDR at RUNTIME (persisted to config.toml ``allowed_ips``).
|
|
331
427
|
|