devops-bot-sdk 1.7.5__tar.gz → 1.7.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/PKG-INFO +1 -1
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/__init__.py +2 -2
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/client.py +1 -1
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/helpdesk_kb.md +5 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ui/api.py +62 -8
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ui/static/app.js +41 -8
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ui/static/styles.css +4 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_agent_listing.py +88 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/README.md +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/devops_bot_sdk.egg-info/SOURCES.txt +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/devops_bot_sdk.egg-info/requires.txt +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/devops_bot_sdk.egg-info/top_level.txt +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/pyproject.toml +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/activity/__init__.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/activity/classifier.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/activity/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/activity/collectors/common.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/activity/collectors/linux_auditd.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/activity/collectors/macos_eslogger.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/activity/collectors/windows_eventlog.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/activity/models.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/activity/os_setup.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/activity/recorder.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/activity/redact.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/activity/settings.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/activity/shipper.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/activity/spool.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/agent_sharing.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/agentd.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/agents/__init__.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/agents/activity.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/agents/builder.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/agents/chat.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/agents/connectors.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/agents/engines/__init__.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/agents/engines/claude_code.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/agents/engines/http.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/agents/engines/mcp.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/agents/engines/n8n.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/agents/engines/shell.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/agents/integrations.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/agents/inventory.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/agents/library/__init__.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/agents/manifest.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/agents/prompt_studio.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/agents/prompt_template.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/agents/prompt_vars.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/agents/run_log.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/agents/schedules.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/agents/secrets.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/agents/ticket_run.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/agents/usage.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/agents/varstore.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/browser_profile.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/claude_usage.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/cli.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/collectors/files.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/collectors/process.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/collectors/screenshot.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/config.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/crucial.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/custom_agents.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/deferred_tickets.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/departments.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/design_verify/__init__.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/design_verify/assets.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/design_verify/bootstrap.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/design_verify/browser.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/design_verify/compare.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/design_verify/loop.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/estimator.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/exceptions.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/fallback_handoff.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/firewall.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/formats.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/git_ops.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/github_fetch.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/github_ingest.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/graph_recon.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/graphify.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/help_actions.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/helpdesk.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/hooks/__init__.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/hooks/crucial_guard.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/intake.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ipc/__init__.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ipc/electron_bridge.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ipc/handlers.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/journal.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/local_exec.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/log_colors.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/memory_guard.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/model_policy.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/models/__init__.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/models/envelope.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/models/requests.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/models/responses.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/models/snapshots.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/phase_timer.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/plan_run.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/platform_compat.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/prd_progress.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/proof_readers.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/providers.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/py.typed +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/resume.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/router.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/run_auto.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/schedule_tickets.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/scheduler.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/self_report.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/self_report_local.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/sse.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/swagger.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/task_file.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/task_types.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/test.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/test_pipeline.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ticket_board.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ticket_fields.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/tickets.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/tiers/__init__.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/tiers/mini_agent.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ui/__init__.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ui/dashboard.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ui/health.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ui/ip_allowlist.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ui/jobs.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ui/models.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ui/runner.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ui/security.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ui/server.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ui/service.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ui/session.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ui/setup_manager.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ui/static/agents.html +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ui/static/chat.html +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ui/static/custom-agents.html +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ui/static/default-agents.html +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ui/static/help.html +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ui/static/index.html +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ui/static/report.html +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ui/static/runs.html +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ui/static/schedules.html +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ui/static/setup.html +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ui/static/tickets.html +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/ui/swagger.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/updater.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/url_access.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/vps_health.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/vps_identity.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/sdk/write_hook.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/setup.cfg +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_activity_claude_runs.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_activity_collectors.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_activity_core.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_activity_live_tracking.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_activity_macos.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_activity_os_setup.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_activity_pipeline.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_agent_activity.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_agent_activity_tracking.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_agent_chat_features.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_agent_crud_cli.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_agent_prompt_async.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_agent_prompt_models.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_agent_run_ticket.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_agent_schedules.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_agent_sharing.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_agent_workspace_settings.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_agents_connectors_builder.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_allowed_tools_config.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_approval_park.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_auto_agent_dispatch.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_auto_ticket_queue.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_browser_profile.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_cheap_tier.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_claude_usage_gate.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_client_jira_projects.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_config_token_diagnostics.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_configure_token_owner.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_console_encoding.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_crash_recovery.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_create_safe_retry.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_deferred_tickets.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_departments.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_done_transition.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_epic_decompose_run.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_epic_inherited_agent_id.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_epic_session_isolation.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_estimator.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_fallback_cli.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_fallback_cli_upgrade.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_firewall_open_on_update.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_formats.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_git_action.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_github_fetch.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_github_ingest.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_graph_recon.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_graphify_build.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_graphify_hook_install.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_health.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_help_actions.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_help_attachments.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_help_questions.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_helpdesk.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_infographic_data.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_intake_job_outcome.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_intake_plan_review.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_journal.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_limit_fallback_handoff.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_local_exec_provider.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_log_colors_backlog.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_mcp_route_contract.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_memory_guard.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_model_policy.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_panel_help.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_panel_ticket_schedules.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_parallel_tickets.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_phase_timer.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_plan_new_command.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_plan_run_activity.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_pr_identity.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_prd_artifact.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_project_path_resolution.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_proof_readers.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_providers.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_recon_session_isolation.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_resume.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_router.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_run_auto_agent_only.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_run_outputs.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_run_verdict_is_honest.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_schedule_api.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_schedule_outputs_and_plans.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_schedule_tickets.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_scheduler.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_scheduler_transient_retry.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_self_report.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_service_token_warning.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_stale_panel_restart.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_swagger_command.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_task_file_and_plan_run.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_task_file_format_doc.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_ticket_board.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_ticket_cli.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_ticket_fields.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_ticket_run_resolve_by_id.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_ticket_schedule_and_agent.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_ticket_speed_fixes.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_tier_flag_parity.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_ui_gate.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_ui_lifespan.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_update_timer.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_updater_extras.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_updater_ignores_cwd.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_updater_pep668.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_updater_reports_the_truth.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_url_access.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_vps_health.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_vps_only_reporting.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_windows_loop_identity.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_windows_panel_task.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_windows_stop_and_pidfile.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_windows_task_paths.py +0 -0
- {devops_bot_sdk-1.7.5 → devops_bot_sdk-1.7.6}/tests/test_write_hook.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""AgentOS Desktop SDK — thin HTTPS/SSE client for the Electron app.
|
|
2
2
|
|
|
3
|
-
Version: 1.7.
|
|
3
|
+
Version: 1.7.6
|
|
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.7.
|
|
37
|
+
__version__ = "1.7.6" # 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
|
|
@@ -2758,7 +2758,7 @@ class BackendClient:
|
|
|
2758
2758
|
url = self._url("/api/v1/agents/shared")
|
|
2759
2759
|
async with httpx.AsyncClient(timeout=15.0) as client:
|
|
2760
2760
|
resp = await client.get(url, headers=self._headers)
|
|
2761
|
-
if resp.status_code
|
|
2761
|
+
if resp.status_code in (404, 405): # 405: an older backend's /agents/{name} catch-all
|
|
2762
2762
|
raise BackendFeatureMissing("Agent sharing", url)
|
|
2763
2763
|
_check_status(resp.status_code, self._base_url)
|
|
2764
2764
|
return resp.json().get("agents", [])
|
|
@@ -104,6 +104,11 @@ stuck In Progress after a crash → `restore-stranded-tickets` (⚠ moves ticket
|
|
|
104
104
|
pending invite per recipient on the AI backend (devops-bot-ai, Mongo `agent_shares`); it reaches them only
|
|
105
105
|
after they `agentos agent accept ID` (or Accept on the Custom Agents page). Accepted
|
|
106
106
|
shares are runnable (`sdk/agents/chat.resolve_agent` reads `GET /api/v1/agents/shared`).
|
|
107
|
+
Accepted shares appear on Custom Agents next to your own (`GET /api/ui/agents` and
|
|
108
|
+
`/api/ui/agents/custom` add them with `source="shared"`, `shared_by`, `via`,
|
|
109
|
+
`share_id`): Run dropdown, installed cards and a "Shared with you" platform group,
|
|
110
|
+
with Run / Remove only. Their inputs come from the backend row (`/api/ui/agents/vars`
|
|
111
|
+
falls back to `resolve_agent`).
|
|
107
112
|
The panel's Share dialog (Custom Agents → Share) has Person / Team / Department tabs;
|
|
108
113
|
each lists from `GET /api/v1/agents/share-targets?kind=…` (users, teams, departments
|
|
109
114
|
in the platform's SQL — names, never ids) and sends the picks as `user:<id>` /
|
|
@@ -466,6 +466,37 @@ async def product_agents(product_id: str = Query(...)) -> dict:
|
|
|
466
466
|
return {"response": [], "product_id": product_id, "error": str(exc)}
|
|
467
467
|
|
|
468
468
|
|
|
469
|
+
async def _shared_agent_rows(client) -> tuple[list[dict], str]:
|
|
470
|
+
"""Agents others shared with you that you ACCEPTED, as ``(rows, error)``.
|
|
471
|
+
|
|
472
|
+
Each row is the backend agent row plus ``source="shared"``, ``shared_by`` (a
|
|
473
|
+
NAME), ``share_id`` (for Remove) and ``via`` ("team X" / "department X").
|
|
474
|
+
A backend without sharing has none — that is not an error to show."""
|
|
475
|
+
from sdk.exceptions import BackendFeatureMissing
|
|
476
|
+
|
|
477
|
+
fetch = getattr(client, "list_shared_agents", None)
|
|
478
|
+
if fetch is None:
|
|
479
|
+
return [], ""
|
|
480
|
+
try:
|
|
481
|
+
rows = await fetch()
|
|
482
|
+
except BackendFeatureMissing:
|
|
483
|
+
return [], ""
|
|
484
|
+
except Exception as exc: # noqa: BLE001
|
|
485
|
+
return [], f"Couldn't load agents shared with you: {exc}"
|
|
486
|
+
out = []
|
|
487
|
+
for r in rows or []:
|
|
488
|
+
if not isinstance(r, dict) or not r.get("name"):
|
|
489
|
+
continue
|
|
490
|
+
by = r.get("shared_by") or {}
|
|
491
|
+
via = r.get("via_team") or {}
|
|
492
|
+
out.append({**r, "source": "shared",
|
|
493
|
+
"shared_by": (by.get("name") or by.get("email") or "") if isinstance(by, dict) else str(by),
|
|
494
|
+
"share_id": r.get("share_id") or "",
|
|
495
|
+
"via": (f"{'department' if via.get('kind') == 'department' else 'team'} {via['name']}"
|
|
496
|
+
if isinstance(via, dict) and via.get("name") else "")})
|
|
497
|
+
return out, ""
|
|
498
|
+
|
|
499
|
+
|
|
469
500
|
@router.get("/agents", dependencies=[_auth])
|
|
470
501
|
async def agents() -> dict:
|
|
471
502
|
"""The caller's own custom agents (``relation_type='User'``) from the backend.
|
|
@@ -480,14 +511,21 @@ async def agents() -> dict:
|
|
|
480
511
|
from sdk.client import BackendClient
|
|
481
512
|
from sdk.exceptions import BackendFeatureMissing
|
|
482
513
|
|
|
514
|
+
client = BackendClient.from_config()
|
|
515
|
+
# Accepted shares sit next to your own (source="shared"), so the Custom Agents
|
|
516
|
+
# page lists and runs them too; your own agent wins a name clash.
|
|
517
|
+
shared, shared_error = await _shared_agent_rows(client)
|
|
483
518
|
try:
|
|
484
|
-
rows = await
|
|
519
|
+
rows = await client.list_custom_agents()
|
|
485
520
|
except BackendFeatureMissing:
|
|
486
|
-
return {"agents":
|
|
487
|
-
|
|
521
|
+
return {"agents": shared, "shared_error": shared_error,
|
|
522
|
+
"error": ("Custom agents aren't available on this "
|
|
523
|
+
"backend — it may be running an older version.")}
|
|
488
524
|
except Exception as exc: # noqa: BLE001 — degrade to empty, surface the reason
|
|
489
|
-
return {"agents":
|
|
490
|
-
|
|
525
|
+
return {"agents": shared, "error": str(exc), "shared_error": shared_error}
|
|
526
|
+
own = {r.get("name") for r in rows}
|
|
527
|
+
return {"agents": rows + [s for s in shared if s["name"] not in own], "error": "",
|
|
528
|
+
"shared_error": shared_error}
|
|
491
529
|
|
|
492
530
|
|
|
493
531
|
def _flat_agent(row: dict, source: str, *, chattable: bool, department_id: str = "") -> dict:
|
|
@@ -640,6 +678,14 @@ async def agent_vars(name: str = Query(...)) -> dict:
|
|
|
640
678
|
from sdk.agents import varstore as agent_varstore
|
|
641
679
|
|
|
642
680
|
manifest = next((m for m in load_agents() if m.name == name), None)
|
|
681
|
+
if manifest is None:
|
|
682
|
+
# Platform agents (yours, department, shared with you) live on the backend,
|
|
683
|
+
# not in ~/.agentos/agents — read their prompt there so the inputs render.
|
|
684
|
+
try:
|
|
685
|
+
from sdk.agents.chat import resolve_agent
|
|
686
|
+
manifest = await resolve_agent(name)
|
|
687
|
+
except Exception: # noqa: BLE001 — unknown / unreachable: no inputs to show
|
|
688
|
+
manifest = None
|
|
643
689
|
names = extract_variables(manifest.prompt) if manifest else []
|
|
644
690
|
values = agent_varstore.load_vars(name)
|
|
645
691
|
return {"name": name, "variables": names,
|
|
@@ -1816,11 +1862,19 @@ async def custom_agents_list() -> dict:
|
|
|
1816
1862
|
backend, which decides what is yours."""
|
|
1817
1863
|
from sdk import custom_agents
|
|
1818
1864
|
|
|
1819
|
-
|
|
1865
|
+
client = _ticket_client()
|
|
1866
|
+
scopes, err = await custom_agents.list_agents(client)
|
|
1820
1867
|
if scopes is None:
|
|
1821
1868
|
raise HTTPException(status_code=502, detail=err)
|
|
1822
|
-
|
|
1823
|
-
|
|
1869
|
+
out = {scope: [custom_agents.editable_view(r) for r in rows]
|
|
1870
|
+
for scope, rows in scopes.items()}
|
|
1871
|
+
# Accepted shares: read-only here (Run / Remove), never Edit or Delete.
|
|
1872
|
+
shared, shared_error = await _shared_agent_rows(client)
|
|
1873
|
+
out["shared"] = [{"id": s.get("id"), "name": s["name"], "status": s.get("status"),
|
|
1874
|
+
"shared_by": s["shared_by"], "via": s["via"], "share_id": s["share_id"]}
|
|
1875
|
+
for s in shared]
|
|
1876
|
+
out["shared_error"] = shared_error
|
|
1877
|
+
return out
|
|
1824
1878
|
|
|
1825
1879
|
|
|
1826
1880
|
@router.post("/agents/custom", dependencies=[_auth])
|
|
@@ -457,8 +457,10 @@
|
|
|
457
457
|
async function act(path, method, okMsg) {
|
|
458
458
|
try { await api(path, { method }); toast(okMsg, "ok"); }
|
|
459
459
|
catch (e) { toast(e.message, "err"); }
|
|
460
|
-
|
|
460
|
+
// Accept / Remove changes which agents you have: the lists above reload too.
|
|
461
|
+
document.dispatchEvent(new CustomEvent("agentos:shares-changed"));
|
|
461
462
|
}
|
|
463
|
+
document.addEventListener("agentos:shares-changed", () => loadShared());
|
|
462
464
|
async function loadShared() {
|
|
463
465
|
const box = $("sh-list");
|
|
464
466
|
document.querySelectorAll("[data-shtab]").forEach(b => b.classList.toggle("primary", b.dataset.shtab === tab));
|
|
@@ -488,7 +490,7 @@
|
|
|
488
490
|
return `<tr><td><div class="nm">${esc(x.agent_name)}</div>${x.message ? `<div class="sub">“${esc(x.message)}”</div>` : ""}</td>
|
|
489
491
|
<td>${esc(who || "?")}${via}</td><td>${pill(x.status)}</td><td class="sub mono">${esc(when)}</td><td class="acts">${acts}</td></tr>`;
|
|
490
492
|
}).join("") + `</tbody></table>`;
|
|
491
|
-
box.querySelectorAll("[data-acc]").forEach(b => b.onclick = () => act(`/api/ui/agents/shares/${b.dataset.acc}/accept`, "POST", "Accepted — it
|
|
493
|
+
box.querySelectorAll("[data-acc]").forEach(b => b.onclick = () => act(`/api/ui/agents/shares/${b.dataset.acc}/accept`, "POST", "Accepted — it's now in your custom agents"));
|
|
492
494
|
box.querySelectorAll("[data-dec]").forEach(b => b.onclick = () => act(`/api/ui/agents/shares/${b.dataset.dec}/decline`, "POST", "Declined"));
|
|
493
495
|
box.querySelectorAll("[data-rm]").forEach(b => b.onclick = () => {
|
|
494
496
|
if (!confirm(tab === "incoming" ? "Remove this shared agent from your list?" : "Revoke this share? They lose access.")) return;
|
|
@@ -1094,17 +1096,27 @@
|
|
|
1094
1096
|
return;
|
|
1095
1097
|
}
|
|
1096
1098
|
caMeta = {}; rows.forEach(a => { caMeta[a.name] = a; });
|
|
1099
|
+
const own = rows.filter(a => a.source !== "shared"), shared = rows.filter(a => a.source === "shared");
|
|
1097
1100
|
const kaSel = $("key-agent");
|
|
1098
1101
|
if (kaSel) {
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
:
|
|
1102
|
+
const opt = (a) => `<option value="${esc(a.name)}">${esc(a.name)}${a.shared_by ? " — from " + esc(a.shared_by) : ""}</option>`;
|
|
1103
|
+
kaSel.innerHTML = !rows.length ? `<option value="">(no agents yet)</option>`
|
|
1104
|
+
: !shared.length ? own.map(opt).join("")
|
|
1105
|
+
: (own.length ? `<optgroup label="Your agents">${own.map(opt).join("")}</optgroup>` : "")
|
|
1106
|
+
+ `<optgroup label="Shared with you">${shared.map(opt).join("")}</optgroup>`;
|
|
1102
1107
|
onAgentSelect();
|
|
1103
1108
|
}
|
|
1104
1109
|
// Custom agents: full control for everyone (view / Edit-content / enable /
|
|
1105
|
-
// disable / delete) — RBAC applies only to department agents.
|
|
1110
|
+
// disable / delete) — RBAC applies only to department agents. Agents shared
|
|
1111
|
+
// with you (accepted) are someone else's: Run and Remove only.
|
|
1106
1112
|
$("local-agents").innerHTML = rows.length ? rows.map(a => {
|
|
1107
1113
|
const enc = encodeURIComponent(a.name);
|
|
1114
|
+
if (a.source === "shared") {
|
|
1115
|
+
const from = `<span class="pill shared">shared</span> <span class="muted" style="font-size:12.5px">from <b>${esc(a.shared_by || "?")}</b>${a.via ? " · via " + esc(a.via) : ""}</span>`;
|
|
1116
|
+
return agentCardHTML(a, `${from}<span class="spacer" style="flex:1"></span>
|
|
1117
|
+
<a class="btn primary" href="/agents.html?agent=${enc}">Run</a>
|
|
1118
|
+
${a.share_id ? `<button class="btn ghost danger" data-unshare="${esc(a.share_id)}" data-name="${esc(a.name)}">Remove</button>` : ""}`);
|
|
1119
|
+
}
|
|
1108
1120
|
const actions = [
|
|
1109
1121
|
`<a class="btn primary" href="/agents.html?agent=${enc}">Run</a>`,
|
|
1110
1122
|
`<button class="btn" data-edit="${esc(a.name)}">Edit</button>`,
|
|
@@ -1117,6 +1129,7 @@
|
|
|
1117
1129
|
: "<div class='empty'>No custom agents assigned to you yet. Create one under <b>Your platform agents</b> below, or in the web app.</div>";
|
|
1118
1130
|
|
|
1119
1131
|
wireContentToggles($("local-agents"));
|
|
1132
|
+
$("local-agents").querySelectorAll("[data-unshare]").forEach(b => b.onclick = () => removeShared(b.dataset.unshare, b.dataset.name));
|
|
1120
1133
|
$("local-agents").querySelectorAll("[data-enable]").forEach(b => b.onclick = () => setStatus(b.dataset.enable, "active"));
|
|
1121
1134
|
$("local-agents").querySelectorAll("[data-disable]").forEach(b => b.onclick = () => setStatus(b.dataset.disable, "disabled"));
|
|
1122
1135
|
$("local-agents").querySelectorAll("[data-del]").forEach(b => b.onclick = async () => {
|
|
@@ -1187,6 +1200,17 @@
|
|
|
1187
1200
|
if ($("key-add-toggle")) $("key-add-toggle").textContent = "+ Add API key";
|
|
1188
1201
|
}
|
|
1189
1202
|
|
|
1203
|
+
// Drop an agent someone shared with you (they can share it again later).
|
|
1204
|
+
async function removeShared(shareId, name) {
|
|
1205
|
+
if (!confirm(`Remove “${name}” from your agents? The owner can share it with you again.`)) return;
|
|
1206
|
+
try {
|
|
1207
|
+
await api(`/api/ui/agents/shares/${encodeURIComponent(shareId)}`, { method: "DELETE" });
|
|
1208
|
+
toast("Removed " + name, "ok");
|
|
1209
|
+
} catch (e) { toast("Remove failed: " + (e.message || ""), "err"); }
|
|
1210
|
+
document.dispatchEvent(new CustomEvent("agentos:shares-changed"));
|
|
1211
|
+
}
|
|
1212
|
+
document.addEventListener("agentos:shares-changed", () => { loadLocalAgents(); cfLoad(); });
|
|
1213
|
+
|
|
1190
1214
|
function onAgentSelect() {
|
|
1191
1215
|
const m = caMeta[$("key-agent") && $("key-agent").value];
|
|
1192
1216
|
if ($("ca-about")) $("ca-about").textContent = m ? (m.description || "No description provided.") : "Pick an agent.";
|
|
@@ -1481,10 +1505,19 @@
|
|
|
1481
1505
|
<button class="btn danger cf-del" data-id="${esc(a.id)}" data-name="${esc(a.name)}">Delete</button>`
|
|
1482
1506
|
: `<span class="muted" style="font-size:12px">read-only</span>`}
|
|
1483
1507
|
</div>`;
|
|
1484
|
-
const personal = d.personal || [], dept = d.department || [];
|
|
1485
|
-
|
|
1508
|
+
const personal = d.personal || [], dept = d.department || [], shared = d.shared || [];
|
|
1509
|
+
const sharedRow = (a) => `<div class="cf-item">
|
|
1510
|
+
<span class="n">${esc(a.name)}</span>
|
|
1511
|
+
<span class="pill shared">shared</span>
|
|
1512
|
+
<span class="muted" style="font-size:12.5px">from <b>${esc(a.shared_by || "?")}</b>${a.via ? " · via " + esc(a.via) : ""}</span>
|
|
1513
|
+
<a class="btn" href="/agents.html?agent=${encodeURIComponent(a.name)}">Run</a>
|
|
1514
|
+
${a.share_id ? `<button class="btn ghost danger cf-unshare" data-id="${esc(a.share_id)}" data-name="${esc(a.name)}">Remove</button>` : ""}
|
|
1515
|
+
</div>`;
|
|
1516
|
+
box.innerHTML = (personal.length || dept.length || shared.length)
|
|
1486
1517
|
? personal.map(a => row(a, true)).join("") + dept.map(a => row(a, false)).join("")
|
|
1518
|
+
+ (shared.length ? `<div class="cf-group">Shared with you</div>` + shared.map(sharedRow).join("") : "")
|
|
1487
1519
|
: `<div class="empty">No platform agents yet — press <b>New agent</b>.</div>`;
|
|
1520
|
+
box.querySelectorAll(".cf-unshare").forEach(b => b.onclick = () => removeShared(b.dataset.id, b.dataset.name));
|
|
1488
1521
|
box.querySelectorAll(".cf-edit").forEach(b => b.onclick = () => cfOpen(b.dataset.id));
|
|
1489
1522
|
box.querySelectorAll(".cf-del").forEach(b => b.onclick = () => cfDelete(b.dataset.id, b.dataset.name));
|
|
1490
1523
|
} catch (e) {
|
|
@@ -857,3 +857,7 @@ select:focus, input[type=number]:focus, input[type=time]:focus, input[type=datet
|
|
|
857
857
|
.hp-ask-row { display: grid; gap: 6px; font-size: 14px; }
|
|
858
858
|
.hp-ask-row input { width: 100%; }
|
|
859
859
|
.hp-asks-send { justify-self: start; }
|
|
860
|
+
|
|
861
|
+
/* Agents shared with you, on the Custom Agents page */
|
|
862
|
+
.pill.shared { background: color-mix(in srgb, var(--violet, #7c3aed) 14%, transparent); color: var(--violet, #7c3aed); }
|
|
863
|
+
.cf-group { font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); padding: 14px 0 4px; }
|
|
@@ -168,3 +168,91 @@ class TestUiAgentsAll:
|
|
|
168
168
|
with patch("sdk.client.BackendClient.from_config", return_value=client):
|
|
169
169
|
out = await api.agents_all(department_id="d-1")
|
|
170
170
|
assert [(a["name"], a["source"]) for a in out["agents"]] == [("Shared", "custom")]
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _shared_row(name, *, by="Javaid Iqbal", via=None, share_id="sh-1"):
|
|
174
|
+
return {**_row(name, variables=("repo",)), "relation_id": "someone-else", "source": "shared",
|
|
175
|
+
"share_id": share_id, "shared_by": {"id": "u-j", "name": by, "email": "j@x"},
|
|
176
|
+
"via_team": via}
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class TestSharedAgentsOnCustomAgentsPage:
|
|
180
|
+
"""Accepted shares appear on Custom Agents next to your own — by NAME, read-only."""
|
|
181
|
+
|
|
182
|
+
@pytest.mark.asyncio
|
|
183
|
+
async def test_installed_list_includes_accepted_shares(self):
|
|
184
|
+
from sdk.ui import api
|
|
185
|
+
|
|
186
|
+
client = SimpleNamespace(
|
|
187
|
+
list_custom_agents=AsyncMock(return_value=[_row("mine"), _row("clash")]),
|
|
188
|
+
list_shared_agents=AsyncMock(return_value=[
|
|
189
|
+
_shared_row("release-notes", via={"id": "d1", "name": "Machine Learning", "kind": "department"}),
|
|
190
|
+
_shared_row("clash")]),
|
|
191
|
+
)
|
|
192
|
+
with patch("sdk.client.BackendClient.from_config", return_value=client):
|
|
193
|
+
out = await api.agents()
|
|
194
|
+
names = [(a["name"], a.get("source")) for a in out["agents"]]
|
|
195
|
+
assert names == [("mine", None), ("clash", None), ("release-notes", "shared")]
|
|
196
|
+
shared = out["agents"][-1]
|
|
197
|
+
assert shared["shared_by"] == "Javaid Iqbal" and shared["share_id"] == "sh-1"
|
|
198
|
+
assert shared["via"] == "department Machine Learning"
|
|
199
|
+
assert out["shared_error"] == ""
|
|
200
|
+
|
|
201
|
+
@pytest.mark.asyncio
|
|
202
|
+
async def test_backend_without_sharing_is_not_an_error(self):
|
|
203
|
+
from sdk.exceptions import BackendFeatureMissing
|
|
204
|
+
from sdk.ui import api
|
|
205
|
+
|
|
206
|
+
client = SimpleNamespace(
|
|
207
|
+
list_custom_agents=AsyncMock(return_value=[_row("mine")]),
|
|
208
|
+
list_shared_agents=AsyncMock(side_effect=BackendFeatureMissing("Agent sharing", "u")),
|
|
209
|
+
)
|
|
210
|
+
with patch("sdk.client.BackendClient.from_config", return_value=client):
|
|
211
|
+
out = await api.agents()
|
|
212
|
+
assert [a["name"] for a in out["agents"]] == ["mine"] and out["shared_error"] == ""
|
|
213
|
+
|
|
214
|
+
@pytest.mark.asyncio
|
|
215
|
+
async def test_platform_list_has_a_shared_group(self):
|
|
216
|
+
from sdk.ui import api
|
|
217
|
+
|
|
218
|
+
client = SimpleNamespace(
|
|
219
|
+
list_gateway_agents_by_scope=AsyncMock(return_value={"personal": [], "department": []}),
|
|
220
|
+
list_shared_agents=AsyncMock(return_value=[_shared_row("release-notes", via={"id": "t", "name": "AI Team"})]),
|
|
221
|
+
)
|
|
222
|
+
with patch("sdk.client.BackendClient.from_config", return_value=client):
|
|
223
|
+
out = await api.custom_agents_list()
|
|
224
|
+
assert out["shared"] == [{"id": "id-release-notes", "name": "release-notes", "status": "active",
|
|
225
|
+
"shared_by": "Javaid Iqbal", "via": "team AI Team", "share_id": "sh-1"}]
|
|
226
|
+
|
|
227
|
+
@pytest.mark.asyncio
|
|
228
|
+
async def test_inputs_of_a_platform_or_shared_agent_render(self):
|
|
229
|
+
from sdk.ui import api
|
|
230
|
+
|
|
231
|
+
client = SimpleNamespace(
|
|
232
|
+
list_custom_agents=AsyncMock(return_value=[]),
|
|
233
|
+
list_department_agents=AsyncMock(return_value=[]),
|
|
234
|
+
list_shared_agents=AsyncMock(return_value=[
|
|
235
|
+
{**_shared_row("release-notes"),
|
|
236
|
+
"prompt": {"system_prompt": "Write notes for {{repo}} since {{tag}}.",
|
|
237
|
+
"variables": [{"name": "repo"}, {"name": "tag"}]}}]),
|
|
238
|
+
)
|
|
239
|
+
with patch("sdk.client.BackendClient.from_config", return_value=client), \
|
|
240
|
+
patch("sdk.agents.manifest.load_agents", return_value=[]), \
|
|
241
|
+
patch("sdk.agents.chat._find_agent", return_value=None):
|
|
242
|
+
out = await api.agent_vars(name="release-notes")
|
|
243
|
+
assert out["variables"] == ["repo", "tag"]
|
|
244
|
+
|
|
245
|
+
@pytest.mark.asyncio
|
|
246
|
+
async def test_405_from_an_old_backend_means_not_deployed(self):
|
|
247
|
+
from sdk.client import BackendClient
|
|
248
|
+
from sdk.exceptions import BackendFeatureMissing
|
|
249
|
+
|
|
250
|
+
client = BackendClient.__new__(BackendClient)
|
|
251
|
+
client._base_url, client._headers = "https://b", {}
|
|
252
|
+
client._url = lambda p: "https://b" + p
|
|
253
|
+
resp = SimpleNamespace(status_code=405, json=lambda: {})
|
|
254
|
+
http = AsyncMock()
|
|
255
|
+
http.__aenter__.return_value.get = AsyncMock(return_value=resp)
|
|
256
|
+
with patch("sdk.client.httpx.AsyncClient", return_value=http):
|
|
257
|
+
with pytest.raises(BackendFeatureMissing):
|
|
258
|
+
await client.list_shared_agents()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|