devops-bot-sdk 1.6.41__tar.gz → 1.6.44__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.41 → devops_bot_sdk-1.6.44}/PKG-INFO +1 -1
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/devops_bot_sdk.egg-info/SOURCES.txt +20 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/__init__.py +2 -2
- devops_bot_sdk-1.6.44/sdk/activity/__init__.py +15 -0
- devops_bot_sdk-1.6.44/sdk/activity/classifier.py +143 -0
- devops_bot_sdk-1.6.44/sdk/activity/collectors/__init__.py +88 -0
- devops_bot_sdk-1.6.44/sdk/activity/collectors/common.py +98 -0
- devops_bot_sdk-1.6.44/sdk/activity/collectors/linux_auditd.py +401 -0
- devops_bot_sdk-1.6.44/sdk/activity/collectors/macos_eslogger.py +356 -0
- devops_bot_sdk-1.6.44/sdk/activity/collectors/windows_eventlog.py +326 -0
- devops_bot_sdk-1.6.44/sdk/activity/models.py +200 -0
- devops_bot_sdk-1.6.44/sdk/activity/os_setup.py +428 -0
- devops_bot_sdk-1.6.44/sdk/activity/recorder.py +198 -0
- devops_bot_sdk-1.6.44/sdk/activity/redact.py +86 -0
- devops_bot_sdk-1.6.44/sdk/activity/settings.py +93 -0
- devops_bot_sdk-1.6.44/sdk/activity/shipper.py +178 -0
- devops_bot_sdk-1.6.44/sdk/activity/spool.py +199 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/agentd.py +32 -2
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/agents/schedules.py +231 -11
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/cli.py +10 -1
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/client.py +23 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/scheduler.py +79 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ui/api.py +47 -20
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ui/models.py +31 -5
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ui/server.py +19 -1
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ui/static/app.js +60 -6
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ui/static/schedules.html +38 -3
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/updater.py +29 -2
- devops_bot_sdk-1.6.44/tests/test_activity_collectors.py +511 -0
- devops_bot_sdk-1.6.44/tests/test_activity_core.py +279 -0
- devops_bot_sdk-1.6.44/tests/test_activity_macos.py +262 -0
- devops_bot_sdk-1.6.44/tests/test_activity_os_setup.py +151 -0
- devops_bot_sdk-1.6.44/tests/test_activity_pipeline.py +408 -0
- devops_bot_sdk-1.6.44/tests/test_schedule_outputs_and_plans.py +226 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_ui_lifespan.py +18 -5
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/README.md +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/devops_bot_sdk.egg-info/requires.txt +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/devops_bot_sdk.egg-info/top_level.txt +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/pyproject.toml +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/agents/__init__.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/agents/activity.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/agents/builder.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/agents/chat.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/agents/connectors.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/agents/engines/__init__.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/agents/engines/claude_code.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/agents/engines/http.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/agents/engines/mcp.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/agents/engines/n8n.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/agents/engines/shell.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/agents/integrations.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/agents/inventory.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/agents/library/__init__.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/agents/manifest.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/agents/prompt_studio.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/agents/prompt_template.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/agents/prompt_vars.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/agents/run_log.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/agents/secrets.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/agents/ticket_run.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/agents/usage.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/agents/varstore.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/browser_profile.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/collectors/files.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/collectors/process.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/collectors/screenshot.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/config.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/crucial.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/departments.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/design_verify/__init__.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/design_verify/assets.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/design_verify/bootstrap.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/design_verify/browser.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/design_verify/compare.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/design_verify/loop.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/estimator.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/exceptions.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/firewall.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/formats.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/git_ops.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/github_fetch.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/github_ingest.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/graph_recon.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/graphify.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/hooks/__init__.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/hooks/crucial_guard.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/intake.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ipc/__init__.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ipc/electron_bridge.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ipc/handlers.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/journal.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/local_exec.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/memory_guard.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/models/__init__.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/models/envelope.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/models/requests.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/models/responses.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/models/snapshots.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/phase_timer.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/plan_run.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/platform_compat.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/prd_progress.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/proof_readers.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/providers.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/py.typed +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/resume.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/router.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/run_auto.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/self_report.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/self_report_local.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/sse.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/swagger.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/task_file.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/task_types.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/test.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/test_pipeline.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ticket_fields.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/tiers/__init__.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/tiers/mini_agent.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ui/__init__.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ui/dashboard.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ui/health.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ui/ip_allowlist.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ui/jobs.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ui/runner.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ui/security.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ui/service.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ui/session.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ui/setup_manager.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ui/static/agents.html +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ui/static/chat.html +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ui/static/custom-agents.html +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ui/static/default-agents.html +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ui/static/index.html +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ui/static/report.html +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ui/static/runs.html +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ui/static/setup.html +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ui/static/styles.css +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ui/static/tickets.html +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/ui/swagger.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/url_access.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/vps_health.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/sdk/write_hook.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/setup.cfg +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_agent_activity.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_agent_activity_tracking.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_agent_chat_features.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_agent_listing.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_agent_prompt_async.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_agent_prompt_models.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_agent_run_ticket.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_agent_schedules.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_agent_workspace_settings.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_agents_connectors_builder.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_allowed_tools_config.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_auto_agent_dispatch.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_browser_profile.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_cheap_tier.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_client_jira_projects.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_config_token_diagnostics.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_configure_token_owner.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_console_encoding.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_crash_recovery.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_departments.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_done_transition.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_epic_decompose_run.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_epic_inherited_agent_id.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_epic_session_isolation.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_estimator.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_firewall_open_on_update.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_formats.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_git_action.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_github_fetch.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_github_ingest.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_graph_recon.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_graphify_build.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_graphify_hook_install.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_health.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_intake_job_outcome.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_intake_plan_review.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_journal.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_local_exec_provider.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_mcp_route_contract.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_memory_guard.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_phase_timer.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_plan_new_command.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_plan_run_activity.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_pr_identity.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_prd_artifact.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_project_path_resolution.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_proof_readers.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_providers.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_recon_session_isolation.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_resume.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_router.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_run_auto_agent_only.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_run_outputs.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_run_verdict_is_honest.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_schedule_api.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_scheduler.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_self_report.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_service_token_warning.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_swagger_command.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_task_file_and_plan_run.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_task_file_format_doc.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_ticket_fields.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_ticket_run_resolve_by_id.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_ticket_speed_fixes.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_tier_flag_parity.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_ui_gate.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_update_timer.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_updater_extras.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_updater_reports_the_truth.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_url_access.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_vps_health.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_windows_loop_identity.py +0 -0
- {devops_bot_sdk-1.6.41 → devops_bot_sdk-1.6.44}/tests/test_write_hook.py +0 -0
|
@@ -51,6 +51,20 @@ sdk/updater.py
|
|
|
51
51
|
sdk/url_access.py
|
|
52
52
|
sdk/vps_health.py
|
|
53
53
|
sdk/write_hook.py
|
|
54
|
+
sdk/activity/__init__.py
|
|
55
|
+
sdk/activity/classifier.py
|
|
56
|
+
sdk/activity/models.py
|
|
57
|
+
sdk/activity/os_setup.py
|
|
58
|
+
sdk/activity/recorder.py
|
|
59
|
+
sdk/activity/redact.py
|
|
60
|
+
sdk/activity/settings.py
|
|
61
|
+
sdk/activity/shipper.py
|
|
62
|
+
sdk/activity/spool.py
|
|
63
|
+
sdk/activity/collectors/__init__.py
|
|
64
|
+
sdk/activity/collectors/common.py
|
|
65
|
+
sdk/activity/collectors/linux_auditd.py
|
|
66
|
+
sdk/activity/collectors/macos_eslogger.py
|
|
67
|
+
sdk/activity/collectors/windows_eventlog.py
|
|
54
68
|
sdk/agents/__init__.py
|
|
55
69
|
sdk/agents/activity.py
|
|
56
70
|
sdk/agents/builder.py
|
|
@@ -123,6 +137,11 @@ sdk/ui/static/schedules.html
|
|
|
123
137
|
sdk/ui/static/setup.html
|
|
124
138
|
sdk/ui/static/styles.css
|
|
125
139
|
sdk/ui/static/tickets.html
|
|
140
|
+
tests/test_activity_collectors.py
|
|
141
|
+
tests/test_activity_core.py
|
|
142
|
+
tests/test_activity_macos.py
|
|
143
|
+
tests/test_activity_os_setup.py
|
|
144
|
+
tests/test_activity_pipeline.py
|
|
126
145
|
tests/test_agent_activity.py
|
|
127
146
|
tests/test_agent_activity_tracking.py
|
|
128
147
|
tests/test_agent_chat_features.py
|
|
@@ -178,6 +197,7 @@ tests/test_run_auto_agent_only.py
|
|
|
178
197
|
tests/test_run_outputs.py
|
|
179
198
|
tests/test_run_verdict_is_honest.py
|
|
180
199
|
tests/test_schedule_api.py
|
|
200
|
+
tests/test_schedule_outputs_and_plans.py
|
|
181
201
|
tests/test_scheduler.py
|
|
182
202
|
tests/test_self_report.py
|
|
183
203
|
tests/test_service_token_warning.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.44
|
|
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.44" # 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
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""VPS activity audit trail.
|
|
2
|
+
|
|
3
|
+
Records what happens on the machine the SDK runs on — commands, logins, processes,
|
|
4
|
+
file and network changes, and every agent run / tool call — classifies it HUMAN /
|
|
5
|
+
AGENT / SYSTEM, and ships it to the backend so it is stored against this VPS.
|
|
6
|
+
|
|
7
|
+
recorder the one safe write API (redacts, never raises)
|
|
8
|
+
spool durable SQLite queue — rows survive crashes and outages
|
|
9
|
+
shipper 5-minute flush loop → devops-bot-ai → devops-bot-be
|
|
10
|
+
classifier pure human-vs-agent decision, identical on every OS
|
|
11
|
+
redact strips secrets from command lines at capture time
|
|
12
|
+
collectors per-OS fact gatherers (opt-in: AGENTOS_ACTIVITY_AUDIT=1)
|
|
13
|
+
|
|
14
|
+
See docs/ACTIVITY_AUDIT.md for the data flow and the OS-side setup.
|
|
15
|
+
"""
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""Decide whether activity on a VPS was done by a HUMAN, an AGENT, or the SYSTEM.
|
|
2
|
+
|
|
3
|
+
The label is per SESSION / process tree, not per event: every event in a session
|
|
4
|
+
inherits it. Collectors on each OS gather *facts* about a process (who it
|
|
5
|
+
descends from, which OS user, whether it has a terminal, how its logon started)
|
|
6
|
+
and this module turns those facts into a verdict. Keeping the decision pure — no
|
|
7
|
+
OS calls — means the same rules run, and are tested, identically on Linux,
|
|
8
|
+
Windows and macOS; only the fact-gathering differs per platform.
|
|
9
|
+
|
|
10
|
+
Signals, strongest first (the confidence is what gets stored and shown):
|
|
11
|
+
|
|
12
|
+
1. descends from a process agentos launched ................ AGENT 95
|
|
13
|
+
2. runs as the dedicated agent OS user ..................... AGENT 90
|
|
14
|
+
3. an interactive login (SSH / RDP / console) .............. HUMAN 85
|
|
15
|
+
… a terminal but no recorded login ...................... HUMAN 70
|
|
16
|
+
4. started by init / service manager / scheduler, no tty ... SYSTEM 80
|
|
17
|
+
5. none of the above ....................................... UNKNOWN 0
|
|
18
|
+
|
|
19
|
+
AGENT beats HUMAN on purpose: a person who SSHes in and starts an agent creates an
|
|
20
|
+
agent subtree; the person's session is recorded separately and linked through
|
|
21
|
+
``initiatedByKey`` so "who launched it" is still answerable.
|
|
22
|
+
|
|
23
|
+
Provenance beats guessing. Anything agentos itself spawns is registered in an
|
|
24
|
+
:class:`AgentProcessRegistry`, which is exact; rows 3-4 are heuristics for
|
|
25
|
+
everything agentos did NOT launch.
|
|
26
|
+
"""
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
from collections.abc import Callable
|
|
30
|
+
from dataclasses import dataclass, field
|
|
31
|
+
|
|
32
|
+
from sdk.activity.models import ActorType, SessionKind
|
|
33
|
+
|
|
34
|
+
#: How far up the process tree to walk before giving up (guards against cycles).
|
|
35
|
+
MAX_ANCESTRY_DEPTH = 64
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass(frozen=True)
|
|
39
|
+
class ProcessFacts:
|
|
40
|
+
"""What a collector observed about a process, in OS-neutral terms."""
|
|
41
|
+
|
|
42
|
+
#: Ancestry reaches a process agentos launched (see AgentProcessRegistry).
|
|
43
|
+
descends_from_agent: bool = False
|
|
44
|
+
agent_run_id: str | None = None
|
|
45
|
+
agent_id: str | None = None
|
|
46
|
+
#: Runs as the dedicated agent OS user (``agentos``; a local account on Windows).
|
|
47
|
+
is_agent_os_user: bool = False
|
|
48
|
+
#: Attached to a terminal / interactive window station.
|
|
49
|
+
has_tty: bool = False
|
|
50
|
+
#: How the logon began, when known: SSH, RDP or CONSOLE.
|
|
51
|
+
login_kind: SessionKind | None = None
|
|
52
|
+
#: Parent chain ends at init/systemd/cron (Linux), services.exe/svchost or the
|
|
53
|
+
#: task scheduler (Windows), or launchd (macOS) — with no terminal.
|
|
54
|
+
spawned_by_service_manager: bool = False
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass(frozen=True)
|
|
58
|
+
class Verdict:
|
|
59
|
+
actor_type: ActorType
|
|
60
|
+
confidence: int
|
|
61
|
+
#: Short machine-readable reasons, e.g. ``["ancestry:agentos", "os_user:agentos"]``.
|
|
62
|
+
reasons: tuple[str, ...] = field(default_factory=tuple)
|
|
63
|
+
session_kind: SessionKind = SessionKind.UNKNOWN
|
|
64
|
+
|
|
65
|
+
def reasons_wire(self) -> dict[str, list[str]]:
|
|
66
|
+
"""Shape stored in ``vps_sessions.actor_reasons``."""
|
|
67
|
+
return {"signals": list(self.reasons)}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def classify(facts: ProcessFacts) -> Verdict:
|
|
71
|
+
"""Apply the signal table to ``facts``."""
|
|
72
|
+
reasons: list[str] = []
|
|
73
|
+
|
|
74
|
+
if facts.descends_from_agent:
|
|
75
|
+
reasons.append("ancestry:agentos")
|
|
76
|
+
if facts.is_agent_os_user:
|
|
77
|
+
reasons.append("os_user:agentos")
|
|
78
|
+
return Verdict(ActorType.AGENT, 95, tuple(reasons), SessionKind.AGENT_RUN)
|
|
79
|
+
|
|
80
|
+
if facts.is_agent_os_user:
|
|
81
|
+
return Verdict(ActorType.AGENT, 90, ("os_user:agentos",), SessionKind.AGENT_RUN)
|
|
82
|
+
|
|
83
|
+
if facts.login_kind in (SessionKind.SSH, SessionKind.RDP, SessionKind.CONSOLE):
|
|
84
|
+
reasons.append(f"login:{facts.login_kind.value}")
|
|
85
|
+
if facts.has_tty:
|
|
86
|
+
reasons.append("tty")
|
|
87
|
+
return Verdict(ActorType.HUMAN, 85, tuple(reasons), facts.login_kind)
|
|
88
|
+
|
|
89
|
+
if facts.has_tty:
|
|
90
|
+
return Verdict(ActorType.HUMAN, 70, ("tty",), SessionKind.CONSOLE)
|
|
91
|
+
|
|
92
|
+
if facts.spawned_by_service_manager:
|
|
93
|
+
return Verdict(
|
|
94
|
+
ActorType.SYSTEM, 80, ("parent:service-manager", "no-tty"), SessionKind.SERVICE
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
return Verdict(ActorType.UNKNOWN, 0, (), SessionKind.UNKNOWN)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@dataclass(frozen=True)
|
|
101
|
+
class AgentRoot:
|
|
102
|
+
pid: int
|
|
103
|
+
run_id: str
|
|
104
|
+
agent_id: str | None = None
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class AgentProcessRegistry:
|
|
108
|
+
"""Pids agentos launched itself, so descendants can be attributed EXACTLY.
|
|
109
|
+
|
|
110
|
+
``register`` is called wherever the SDK spawns an agent process (the ``claude -p``
|
|
111
|
+
subprocess, agentd jobs). Collectors then ask :meth:`lookup` with a pid and a
|
|
112
|
+
``parent_of`` function, and get the owning run if any ancestor is a root.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
def __init__(self) -> None:
|
|
116
|
+
self._roots: dict[int, AgentRoot] = {}
|
|
117
|
+
|
|
118
|
+
def register(self, pid: int, run_id: str, agent_id: str | None = None) -> None:
|
|
119
|
+
self._roots[pid] = AgentRoot(pid, run_id, agent_id)
|
|
120
|
+
|
|
121
|
+
def unregister(self, pid: int) -> None:
|
|
122
|
+
self._roots.pop(pid, None)
|
|
123
|
+
|
|
124
|
+
def lookup(self, pid: int, parent_of: Callable[[int], int | None]) -> AgentRoot | None:
|
|
125
|
+
"""The agent run ``pid`` belongs to, by walking its ancestry, or ``None``.
|
|
126
|
+
|
|
127
|
+
``parent_of(pid)`` returns the parent pid, or ``None``/``0`` at the top. A
|
|
128
|
+
pid that has already exited can't be walked, so a collector that sees events
|
|
129
|
+
late should cache ppid at exec time.
|
|
130
|
+
"""
|
|
131
|
+
seen: set[int] = set()
|
|
132
|
+
current: int | None = pid
|
|
133
|
+
for _ in range(MAX_ANCESTRY_DEPTH):
|
|
134
|
+
if current is None or current <= 0 or current in seen:
|
|
135
|
+
return None
|
|
136
|
+
if current in self._roots:
|
|
137
|
+
return self._roots[current]
|
|
138
|
+
seen.add(current)
|
|
139
|
+
current = parent_of(current)
|
|
140
|
+
return None
|
|
141
|
+
|
|
142
|
+
def __len__(self) -> int:
|
|
143
|
+
return len(self._roots)
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""OS-level collectors — opt-in (``AGENTOS_ACTIVITY_AUDIT=1``).
|
|
2
|
+
|
|
3
|
+
Linux auditd sdk.activity.collectors.linux_auditd
|
|
4
|
+
Windows Security log sdk.activity.collectors.windows_eventlog
|
|
5
|
+
macOS Endpoint Security, through Apple's own ``eslogger`` (macOS 13+)
|
|
6
|
+
sdk.activity.collectors.macos_eslogger
|
|
7
|
+
|
|
8
|
+
``start()`` has the same contract as the other crons the UI lifespan starts
|
|
9
|
+
(``sdk.vps_health.start`` …): it returns an asyncio task, or ``None`` when disabled
|
|
10
|
+
or unsupported, and never raises into the panel.
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import asyncio
|
|
15
|
+
import logging
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
from sdk.activity import settings
|
|
19
|
+
from sdk.activity.models import Os, current_os
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger("sdk.activity.collectors")
|
|
22
|
+
|
|
23
|
+
#: Local account agentos runs agents as (see docs/ACTIVITY_AUDIT.md).
|
|
24
|
+
AGENT_OS_USER = "agentos"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def make_collector(os_name: Os | None = None) -> Any | None:
|
|
28
|
+
"""The collector for this OS, or ``None`` when there isn't one."""
|
|
29
|
+
os_name = os_name or current_os()
|
|
30
|
+
if os_name is Os.LINUX:
|
|
31
|
+
from sdk.activity.collectors.linux_auditd import AuditdCollector, AuditdParser
|
|
32
|
+
|
|
33
|
+
agent_uid: int | None = None
|
|
34
|
+
try:
|
|
35
|
+
import pwd
|
|
36
|
+
|
|
37
|
+
agent_uid = pwd.getpwnam(AGENT_OS_USER).pw_uid
|
|
38
|
+
except Exception: # noqa: BLE001 — no such user (yet): heuristics 1/3/4 still apply
|
|
39
|
+
agent_uid = None
|
|
40
|
+
return AuditdCollector(parser=AuditdParser(agent_uid=agent_uid))
|
|
41
|
+
if os_name is Os.WINDOWS:
|
|
42
|
+
from sdk.activity.collectors.windows_eventlog import (
|
|
43
|
+
WindowsEventLogCollector,
|
|
44
|
+
WindowsEventProcessor,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
return WindowsEventLogCollector(WindowsEventProcessor(agent_user=AGENT_OS_USER))
|
|
48
|
+
if os_name is Os.MACOS:
|
|
49
|
+
from sdk.activity.collectors.macos_eslogger import EsloggerCollector, EsloggerParser
|
|
50
|
+
|
|
51
|
+
mac_uid: int | None = None
|
|
52
|
+
try:
|
|
53
|
+
import pwd
|
|
54
|
+
|
|
55
|
+
mac_uid = pwd.getpwnam(AGENT_OS_USER).pw_uid
|
|
56
|
+
except Exception: # noqa: BLE001 — no such user (yet)
|
|
57
|
+
mac_uid = None
|
|
58
|
+
return EsloggerCollector(parser=EsloggerParser(agent_uid=mac_uid))
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
async def run_forever(collector: Any) -> None:
|
|
63
|
+
"""Run a blocking collector in a worker thread until cancelled."""
|
|
64
|
+
try:
|
|
65
|
+
await asyncio.to_thread(collector.run)
|
|
66
|
+
except asyncio.CancelledError:
|
|
67
|
+
collector.stop() # the thread exits after its current poll
|
|
68
|
+
raise
|
|
69
|
+
except Exception:
|
|
70
|
+
logger.exception("activity collector crashed")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def start() -> asyncio.Task[None] | None:
|
|
74
|
+
if not (settings.enabled() and settings.audit_enabled()):
|
|
75
|
+
return None
|
|
76
|
+
try:
|
|
77
|
+
collector = make_collector()
|
|
78
|
+
except Exception as exc: # noqa: BLE001
|
|
79
|
+
logger.warning("activity collector could not be created: %s", exc)
|
|
80
|
+
return None
|
|
81
|
+
if collector is None:
|
|
82
|
+
logger.warning(
|
|
83
|
+
"AGENTOS_ACTIVITY_AUDIT is on but there is no OS collector for %s yet; "
|
|
84
|
+
"agent runs are still recorded", current_os().value,
|
|
85
|
+
)
|
|
86
|
+
return None
|
|
87
|
+
logger.info("activity collector starting (%s)", type(collector).__name__)
|
|
88
|
+
return asyncio.ensure_future(run_forever(collector))
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""Session bookkeeping shared by the OS collectors.
|
|
2
|
+
|
|
3
|
+
A collector turns raw OS records into (a) sessions — logins, and agent-attributed
|
|
4
|
+
process trees — and (b) events inside them. This module owns the part that is the
|
|
5
|
+
same on every OS: deciding a session's label with the classifier, recording it once,
|
|
6
|
+
and keeping the key → verdict map so later events can inherit it.
|
|
7
|
+
|
|
8
|
+
An AGENT verdict needs an ``agentRunId`` on the wire: devops-bot-be only keeps an
|
|
9
|
+
AGENT claim that points at an AGENT session carrying that run id on the same VPS.
|
|
10
|
+
When agentos launched the process, the run id is the real one (``agent-run:<id>``,
|
|
11
|
+
recorded by the agent runner). When the label came from the OS user alone there is
|
|
12
|
+
no such run, so :meth:`SessionBook.ensure` synthesises one (``os:<session key>``) and
|
|
13
|
+
records the session that backs it — the claim is then self-consistent and the
|
|
14
|
+
reason it was made (``os_user:agentos``) is kept in ``actorReasons``.
|
|
15
|
+
"""
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from dataclasses import dataclass
|
|
19
|
+
|
|
20
|
+
from sdk.activity import recorder
|
|
21
|
+
from sdk.activity.classifier import Verdict
|
|
22
|
+
from sdk.activity.models import ActivitySession, ActorType, SessionKind
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True)
|
|
26
|
+
class SessionInfo:
|
|
27
|
+
key: str
|
|
28
|
+
verdict: Verdict
|
|
29
|
+
kind: SessionKind
|
|
30
|
+
agent_run_id: str | None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class SessionBook:
|
|
34
|
+
"""Tracks sessions a collector has already recorded, by key."""
|
|
35
|
+
|
|
36
|
+
def __init__(self) -> None:
|
|
37
|
+
self._sessions: dict[str, SessionInfo] = {}
|
|
38
|
+
|
|
39
|
+
def get(self, key: str) -> SessionInfo | None:
|
|
40
|
+
return self._sessions.get(key)
|
|
41
|
+
|
|
42
|
+
def ensure(
|
|
43
|
+
self,
|
|
44
|
+
key: str,
|
|
45
|
+
*,
|
|
46
|
+
started: float,
|
|
47
|
+
verdict: Verdict,
|
|
48
|
+
kind: SessionKind | None = None,
|
|
49
|
+
os_user: str | None = None,
|
|
50
|
+
remote_ip: str | None = None,
|
|
51
|
+
agent_run_id: str | None = None,
|
|
52
|
+
agent_id: str | None = None,
|
|
53
|
+
initiated_by_key: str | None = None,
|
|
54
|
+
) -> SessionInfo:
|
|
55
|
+
"""Record the session the first time it is seen; return it either way."""
|
|
56
|
+
known = self._sessions.get(key)
|
|
57
|
+
if known:
|
|
58
|
+
return known
|
|
59
|
+
|
|
60
|
+
run_id = agent_run_id
|
|
61
|
+
if verdict.actor_type is ActorType.AGENT and not run_id:
|
|
62
|
+
run_id = f"os:{key}" # see module docstring
|
|
63
|
+
|
|
64
|
+
info = SessionInfo(key, verdict, kind or verdict.session_kind, run_id)
|
|
65
|
+
self._sessions[key] = info
|
|
66
|
+
recorder.record_session(
|
|
67
|
+
ActivitySession(
|
|
68
|
+
externalKey=key,
|
|
69
|
+
startedAt=started,
|
|
70
|
+
kind=info.kind,
|
|
71
|
+
actorType=verdict.actor_type,
|
|
72
|
+
actorConfidence=verdict.confidence,
|
|
73
|
+
actorReasons=verdict.reasons_wire(),
|
|
74
|
+
osUser=os_user,
|
|
75
|
+
remoteIp=remote_ip,
|
|
76
|
+
agentRunId=run_id,
|
|
77
|
+
agentId=agent_id,
|
|
78
|
+
initiatedByKey=initiated_by_key,
|
|
79
|
+
)
|
|
80
|
+
)
|
|
81
|
+
return info
|
|
82
|
+
|
|
83
|
+
def end(self, key: str, ended: float) -> None:
|
|
84
|
+
"""Close a session. A no-op for a session we never saw open."""
|
|
85
|
+
info = self._sessions.pop(key, None)
|
|
86
|
+
if info is None:
|
|
87
|
+
return
|
|
88
|
+
recorder.record_session(
|
|
89
|
+
ActivitySession(
|
|
90
|
+
externalKey=key,
|
|
91
|
+
startedAt=ended, # ignored by the backend on update; the model requires it
|
|
92
|
+
kind=info.kind,
|
|
93
|
+
actorType=info.verdict.actor_type,
|
|
94
|
+
actorConfidence=info.verdict.confidence,
|
|
95
|
+
agentRunId=info.agent_run_id,
|
|
96
|
+
endedAt=ended,
|
|
97
|
+
)
|
|
98
|
+
)
|